diff --git a/.github/workflows/deploy-clean.yml b/.github/workflows/deploy-clean.yml new file mode 100644 index 0000000..235b60d --- /dev/null +++ b/.github/workflows/deploy-clean.yml @@ -0,0 +1,106 @@ +# # +# @type github workflow +# @desc cleans up the list of deployments in the environment history +# @author Aetherinox +# @url https://github.com/Aetherinox +# # + +name: "⚙️ Deploy › Clean" +run-name: "⚙️ Deploy › Clean" + +# # +# triggers +# # + +on: + workflow_dispatch: + +# # +# environment variables +# # + +env: + BOT_NAME_1: AdminServ + BOT_NAME_2: AdminServX + BOT_NAME_3: EuropaServ + BOT_NAME_DEPENDABOT: dependabot[bot] + LABELS_JSON: | + [ + { "name": "AC › Changes Made", "color": "8F1784", "description": "Requested changes have been made and are pending a re-scan" }, + { "name": "AC › Changes Required", "color": "8F1784", "description": "Requires changes to be made to the package before being accepted" }, + { "name": "AC › Failed", "color": "a61f2d", "description": "Autocheck failed to run through a complete cycle, requires investigation" }, + { "name": "AC › Needs Rebase", "color": "8F1784", "description": "Due to the permissions on the requesting repo, this pull request must be rebased by the author" }, + { "name": "AC › Passed", "color": "146b4a", "description": "Ready to be reviewed" }, + { "name": "AC › Review Required", "color": "8F1784", "description": "PR needs to be reviewed by another person, after the requested changes have been made" }, + { "name": "AC › Security Warning", "color": "761620", "description": "Does not conform to developer policies, or includes potentially dangerous code" }, + { "name": "AC › Skipped Scan", "color": "8F1784", "description": "Author has skipped code scan" }, + { "name": "Status 𐄂 Duplicate", "color": "75536b", "description": "Issue or pull request already exists" }, + { "name": "Status 𐄂 Accepted", "color": "2e7539", "description": "This pull request has been accepted" }, + { "name": "Status 𐄂 Autoclosed", "color": "3E0915", "description": "Originally stale and was autoclosed for no activity" }, + { "name": "Status 𐄂 Denied", "color": "ba4058", "description": "Pull request has been denied" }, + { "name": "Status 𐄂 Locked", "color": "550F45", "description": "Automatically locked by AdminServ for a prolonged period of inactivity" }, + { "name": "Status 𐄂 Need Info", "color": "2E3C4C", "description": "Not enough information to resolve" }, + { "name": "Status 𐄂 No Action", "color": "030406", "description": "Closed without any action being taken" }, + { "name": "Status 𐄂 Pending", "color": "984b12", "description": "Pending pull request" }, + { "name": "Status 𐄂 Released", "color": "1b6626", "description": "Issues or PR has been implemented and is now live" }, + { "name": "Status 𐄂 Reopened", "color": "8a6f14", "description": "A previously closed PR which has been re-opened" }, + { "name": "Status 𐄂 Review", "color": "9e1451", "description": "Currently pending review" }, + { "name": "Status 𐄂 Stale", "color": "928282", "description": "Has not had any activity in over 30 days" }, + { "name": "Type ◦ Bug", "color": "9a2c2c", "description": "Something isn't working" }, + { "name": "Type ◦ Dependency", "color": "243759", "description": "Item is associated to dependency" }, + { "name": "Type ◦ Docs", "color": "0e588d", "description": "Improvements or modifications to docs" }, + { "name": "Type ◦ Feature", "color": "3c4e93", "description": "Feature request" }, + { "name": "Type ◦ Git Action", "color": "030406", "description": "GitHub Action / workflow" }, + { "name": "Type ◦ Pull Request", "color": "8F1784", "description": "Normal pull request" }, + { "name": "Type ◦ Roadmap", "color": "8F1784", "description": "Feature or bug currently planned for implementation" }, + { "name": "Type ◦ Internal", "color": "A51994", "description": "Assigned items are for internal developer use" }, + { "name": "Build ◦ Desktop", "color": "c7ca4a", "description": "Specific to desktop" }, + { "name": "Build ◦ Linux", "color": "c7ca4a", "description": "Specific to Linux" }, + { "name": "Build ◦ MacOS", "color": "c7ca4a", "description": "Specific to MacOS" }, + { "name": "Build ◦ Mobile", "color": "c7ca4a", "description": "Specific to mobile" }, + { "name": "Build ◦ Web", "color": "c7ca4a", "description": "Specific to web" }, + { "name": "Build ◦ Windows", "color": "c7ca4a", "description": "Specific to Windows" }, + { "name": "› API", "color": "F99B50", "description": "Plugin API, CLI, browser JS API" }, + { "name": "› Auto-type", "color": "9141E0", "description": "Auto-type functionality in desktop apps" }, + { "name": "› Browser", "color": "9141E0", "description": "Browser plugins and passing data to <=> from app" }, + { "name": "› Customization", "color": "E3F0FC", "description": "Customizations: plugins, themes, configs" }, + { "name": "› Design", "color": "FA70DE", "description": "Design related queries" }, + { "name": "› Dist", "color": "FA70DE", "description": "Installers and other forms of software distribution" }, + { "name": "› Enterprise", "color": "11447a", "description": "Issues about collaboration, administration, and so on" }, + { "name": "› Hardware", "color": "5a7503", "description": "YubiKey, other tokens, biometrics" }, + { "name": "› Import/Export", "color": "F5FFCC", "description": "Import from and export to different file formats" }, + { "name": "› Improvement", "color": "185c98", "description": "Enhance an existing feature" }, + { "name": "› Performance", "color": "006b75", "description": "Web and desktop performance issues" }, + { "name": "› Plugin Request", "color": "FCE9CA", "description": "Requested changes should be implemented as a plugin" }, + { "name": "› Security", "color": "F75D39", "description": "Security issues" }, + { "name": "› Self-Hosting", "color": "fad8c7", "description": "Self-hosting installations and configs" }, + { "name": "› Storage", "color": "5319e7", "description": "Storage providers: Dropbox, Google, WebDAV, etc." }, + { "name": "› Updater", "color": "1BADDE", "description": "Auto-updater issues" }, + { "name": "› UX", "color": "1BADDE", "description": "UX and usability" }, + { "name": "› Website", "color": "fef2c0", "description": "Website related issues" }, + { "name": "⚠ Urgent", "color": "a8740e", "description": "Requires urgent attention" }, + { "name": "⚠ Announcement", "color": "DB4712", "description": "Announcements" }, + { "name": "📰 Progress Report", "color": "392297", "description": "Development updates" }, + { "name": "📦 Release", "color": "277542", "description": "Release announcements" }, + { "name": "✔️ Poll", "color": "972255", "description": "Community polls" }, + { "name": "❔ Question", "color": "FFFFFF", "description": "All questions" } + ] + +# # +# jobs +# # + +jobs: + cleanup: + runs-on: ubuntu-latest + permissions: write-all + + steps: + - name: >- + ⚙️ Deployments › Clean + uses: Aetherinox/delete-deploy-env-action@v3 + with: + token: ${{ secrets.SELF_TOKEN_CL }} + environment: orion + onlyRemoveDeployments: true + delay: "1000" \ No newline at end of file diff --git a/.github/workflows/issues-accept.yml b/.github/workflows/issues-accept.yml new file mode 100644 index 0000000..7384306 --- /dev/null +++ b/.github/workflows/issues-accept.yml @@ -0,0 +1,72 @@ +# # +# @type github workflow +# @desc adds a label to a PR when the command "/accept" is typed in the issue comments +# do not attempt to use env variables in if condition. +# do not accept to change GITHUB_TOKEN. +# @author Aetherinox +# @url https://github.com/Aetherinox +# # + +name: "🎫 Issue › Accept" +run-name: "🎫 Issue › Accept" + +# # +# triggers +# # + +on: + issue_comment: + types: [created] + +# # +# environment variables +# # + +env: + LABEL_ACCEPT: "Status 𐄂 Accepted" + + BOT_NAME_1: AdminServ + BOT_NAME_2: AdminServX + BOT_NAME_3: EuropaServ + BOT_NAME_DEPENDABOT: dependabot[bot] + +# # +# jobs +# # + +jobs: + + # # + # Job [ Deploy ] + # # + + deploy: + if: contains(github.event.comment.body, '/accept') && github.event.comment.user.login == 'Aetherinox' + runs-on: ubuntu-latest + steps: + + # # + # Add Label to accepted PR + # # + + - name: >- + 🏷️ Assign Label › ${{ env.LABEL_ACCEPT }} + run: gh issue edit "$NUMBER" --add-label "$LABELS" + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_REPO: ${{ github.repository }} + NUMBER: ${{ github.event.issue.number }} + LABELS: ${{ env.LABEL_ACCEPT }} + + # # + # Add assignee to accepted PR + # # + + - name: >- + 🏷️ Assign Assignee › ${{ github.repository_owner }} + run: gh issue edit "$NUMBER" --add-assignee "$ASSIGNEE" + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_REPO: ${{ github.repository }} + NUMBER: ${{ github.event.issue.number }} + ASSIGNEE: ${{ github.repository_owner }} diff --git a/.github/workflows/issues-new.yml b/.github/workflows/issues-new.yml new file mode 100644 index 0000000..7f237ad --- /dev/null +++ b/.github/workflows/issues-new.yml @@ -0,0 +1,891 @@ +# # +# @type github workflow +# @desc searches a new issues title and body for certain keywords and assigns a label +# sets the assignee for the issue to the repository owner +# @author Aetherinox +# @url https://github.com/Aetherinox +# +# requires the following labels to be created in your repo: +# - bug +# - feature +# - urgent +# - roadmap +# # + +name: "🎫 Issue › New" +run-name: "🎫 Issue › New › ${{ github.event.issue.number }}: ${{ github.event.issue.title }}" + +# # +# triggers +# # + +on: + issues: + types: + - reopened + - opened + +# # +# environment variables +# # + +env: + PREFIX_BUG: "Bug" + PREFIX_DEPENDENCY: "Dependency" + PREFIX_DOCS: "Docs" + PREFIX_FEATURE: "Feature" + PREFIX_GIT: "Git Action" + PREFIX_PR: "PR" + PREFIX_ROADMAP: "Roadmap" + PREFIX_INTERNAL: "Internal" + PREFIX_URGENT: "Urgent" + + LABEL_BUG: "Type ◦ Bug" + LABEL_DEPENDENCY: "Type ◦ Dependency" + LABEL_DOCS: "Type ◦ Docs" + LABEL_FEATURE: "Type ◦ Feature" + LABEL_GIT: "Type ◦ Git Action" + LABEL_PR: "Type ◦ Pull Request" + LABEL_ROADMAP: "Type ◦ Roadmap" + LABEL_INTERNAL: "Type ◦ Git Action" + LABEL_URGENT: "⚠ Urgent" + + BOT_NAME_1: AdminServ + BOT_NAME_2: AdminServX + BOT_NAME_3: EuropaServ + BOT_NAME_DEPENDABOT: dependabot[bot] + + LABELS_JSON: | + [ + { "name": "AC › Changes Made", "color": "8F1784", "description": "Requested changes have been made and are pending a re-scan" }, + { "name": "AC › Changes Required", "color": "8F1784", "description": "Requires changes to be made to the package before being accepted" }, + { "name": "AC › Failed", "color": "a61f2d", "description": "Autocheck failed to run through a complete cycle, requires investigation" }, + { "name": "AC › Needs Rebase", "color": "8F1784", "description": "Due to the permissions on the requesting repo, this pull request must be rebased by the author" }, + { "name": "AC › Passed", "color": "146b4a", "description": "Ready to be reviewed" }, + { "name": "AC › Review Required", "color": "8F1784", "description": "PR needs to be reviewed by another person, after the requested changes have been made" }, + { "name": "AC › Security Warning", "color": "761620", "description": "Does not conform to developer policies, or includes potentially dangerous code" }, + { "name": "AC › Skipped Scan", "color": "8F1784", "description": "Author has skipped code scan" }, + { "name": "Status 𐄂 Duplicate", "color": "75536b", "description": "Issue or pull request already exists" }, + { "name": "Status 𐄂 Accepted", "color": "2e7539", "description": "This pull request has been accepted" }, + { "name": "Status 𐄂 Autoclosed", "color": "3E0915", "description": "Originally stale and was autoclosed for no activity" }, + { "name": "Status 𐄂 Denied", "color": "ba4058", "description": "Pull request has been denied" }, + { "name": "Status 𐄂 Locked", "color": "550F45", "description": "Automatically locked by AdminServ for a prolonged period of inactivity" }, + { "name": "Status 𐄂 Need Info", "color": "2E3C4C", "description": "Not enough information to resolve" }, + { "name": "Status 𐄂 No Action", "color": "030406", "description": "Closed without any action being taken" }, + { "name": "Status 𐄂 Pending", "color": "984b12", "description": "Pending pull request" }, + { "name": "Status 𐄂 Released", "color": "1b6626", "description": "Issues or PR has been implemented and is now live" }, + { "name": "Status 𐄂 Reopened", "color": "8a6f14", "description": "A previously closed PR which has been re-opened" }, + { "name": "Status 𐄂 Review", "color": "9e1451", "description": "Currently pending review" }, + { "name": "Status 𐄂 Stale", "color": "928282", "description": "Has not had any activity in over 30 days" }, + { "name": "Type ◦ Bug", "color": "9a2c2c", "description": "Something isn't working" }, + { "name": "Type ◦ Dependency", "color": "243759", "description": "Item is associated to dependency" }, + { "name": "Type ◦ Docs", "color": "0e588d", "description": "Improvements or modifications to docs" }, + { "name": "Type ◦ Feature", "color": "3c4e93", "description": "Feature request" }, + { "name": "Type ◦ Git Action", "color": "030406", "description": "GitHub Action / workflow" }, + { "name": "Type ◦ Pull Request", "color": "8F1784", "description": "Normal pull request" }, + { "name": "Type ◦ Roadmap", "color": "8F1784", "description": "Feature or bug currently planned for implementation" }, + { "name": "Type ◦ Internal", "color": "A51994", "description": "Assigned items are for internal developer use" }, + { "name": "Build ◦ Desktop", "color": "c7ca4a", "description": "Specific to desktop" }, + { "name": "Build ◦ Linux", "color": "c7ca4a", "description": "Specific to Linux" }, + { "name": "Build ◦ MacOS", "color": "c7ca4a", "description": "Specific to MacOS" }, + { "name": "Build ◦ Mobile", "color": "c7ca4a", "description": "Specific to mobile" }, + { "name": "Build ◦ Web", "color": "c7ca4a", "description": "Specific to web" }, + { "name": "Build ◦ Windows", "color": "c7ca4a", "description": "Specific to Windows" }, + { "name": "› API", "color": "F99B50", "description": "Plugin API, CLI, browser JS API" }, + { "name": "› Auto-type", "color": "9141E0", "description": "Auto-type functionality in desktop apps" }, + { "name": "› Browser", "color": "9141E0", "description": "Browser plugins and passing data to <=> from app" }, + { "name": "› Customization", "color": "E3F0FC", "description": "Customizations: plugins, themes, configs" }, + { "name": "› Design", "color": "FA70DE", "description": "Design related queries" }, + { "name": "› Dist", "color": "FA70DE", "description": "Installers and other forms of software distribution" }, + { "name": "› Enterprise", "color": "11447a", "description": "Issues about collaboration, administration, and so on" }, + { "name": "› Hardware", "color": "5a7503", "description": "YubiKey, other tokens, biometrics" }, + { "name": "› Import/Export", "color": "F5FFCC", "description": "Import from and export to different file formats" }, + { "name": "› Improvement", "color": "185c98", "description": "Enhance an existing feature" }, + { "name": "› Performance", "color": "006b75", "description": "Web and desktop performance issues" }, + { "name": "› Plugin Request", "color": "FCE9CA", "description": "Requested changes should be implemented as a plugin" }, + { "name": "› Security", "color": "F75D39", "description": "Security issues" }, + { "name": "› Self-Hosting", "color": "fad8c7", "description": "Self-hosting installations and configs" }, + { "name": "› Storage", "color": "5319e7", "description": "Storage providers: Dropbox, Google, WebDAV, etc." }, + { "name": "› Updater", "color": "1BADDE", "description": "Auto-updater issues" }, + { "name": "› UX", "color": "1BADDE", "description": "UX and usability" }, + { "name": "› Website", "color": "fef2c0", "description": "Website related issues" }, + { "name": "⚠ Urgent", "color": "a8740e", "description": "Requires urgent attention" }, + { "name": "⚠ Announcement", "color": "DB4712", "description": "Announcements" }, + { "name": "📰 Progress Report", "color": "392297", "description": "Development updates" }, + { "name": "📦 Release", "color": "277542", "description": "Release announcements" }, + { "name": "✔️ Poll", "color": "972255", "description": "Community polls" }, + { "name": "❔ Question", "color": "FFFFFF", "description": "All questions" } + ] + +# # +# jobs +# # + +jobs: + + # # + # Job [ Verify / Create Labels ] + # + # This job will ensure you have labels already created in your repo. + # All labels come from the JSON table LABELS_JSON. + # # + + job-labels-create: + name: >- + 🎫 Labels › Verify Existing + runs-on: ubuntu-latest + steps: + + # # + # [ Create Labels ] Start + # # + + - name: >- + ✅ Start + id: task_label_create_start + run: | + echo "Assigning labels and assignees" + + # # + # [ Create Labels ] Checkout + # # + + - name: >- + ☑️ Checkout + id: task_label_create_checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + # # + # [ Create Labels ] Verify Existing Labels + # # + + - name: >- + 🏷️ Verify Existing Labels + id: task_label_create_verify + uses: actions/github-script@v7 + with: + github-token: ${{ secrets.ADMINSERV_TOKEN_CL }} + script: | + const labels = JSON.parse( process.env.LABELS_JSON ); + for ( const label of labels ) + { + try + { + await github.rest.issues.createLabel( + { + owner: context.repo.owner, + repo: context.repo.repo, + name: label.name, + description: label.description || '', + color: label.color + }); + } + catch ( err ) + { + if ( err.status === 422 ) + { + console.log( `Label '${label.name}' already exists. Skipping.` ); + } + else + { + console.error( `Error creating label '${label.name}': ${err}` ); + } + } + } + + # # + # Job [ Assign Labels ] + # # + + job-assign-labels: + name: >- + 🏷️ Labels › Assign + needs: + - job-labels-create + runs-on: ubuntu-latest + permissions: + contents: 'read' + id-token: 'write' + issues: 'write' + steps: + + # # + # Assign > Get Issue Title + # # + + - name: >- + 🏷️ Get Issue Title + uses: actions/github-script@v7 + id: task_get_title + with: + github-token: ${{ secrets.ADMINSERV_TOKEN_CL }} + script: | + let iss_title = `${ context.payload.issue.title }`; + + core.setOutput( 'issue_title', iss_title ) + core.info( `Setting env issue title: ${ iss_title }` ) + + console.log( "\n\n" ) + + # # + # Labels > Bugs + # + # Title of issue is carried over from the previous step. + # # + + - name: >- + 🏷️ ${{ env.PREFIX_BUG }} › Assignment + uses: actions/github-script@v7 + id: task_issues_bugs + with: + github-token: ${{ secrets.ADMINSERV_TOKEN_CL }} + script: | + + const issueLabels = await github.rest.issues.listLabelsOnIssue( + { + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number + }); + + let add_labels = issueLabels.data.map( label => label.name ); + + let iss_title = `${{ steps.task_get_title.outputs.issue_title }}` || `${ context.payload.issue.title }`; + let iss_body = `${ context.payload.issue.body }`; + let iss_author = `${ context.payload.issue.user.login }`; + + const iss_title_lc = iss_title.toLowerCase( ); + + console.log( "Bug Title ..................... " + iss_title ) + console.log( "Bug Output .................... " + `${{ steps.task_get_title.outputs.issue_title }}` ) + console.log( "Bug Payload ................... " + `${ context.payload.issue.title }` ) + + /* + Tags + */ + + const bug_tag = `${{ env.PREFIX_BUG }}:`; + const bug_lbl = `${{ env.LABEL_BUG }}`; + const feat_tag = `${{ env.PREFIX_FEATURE }}:`; + const feat_lbl = `${{ env.LABEL_FEATURE }}`; + const urgn_tag = `${{ env.PREFIX_URGENT }}:`; + const urgn_lbl = `${{ env.LABEL_URGENT }}`; + const road_tag = `${{ env.PREFIX_ROADMAP }}:`; + const road_lbl = `${{ env.LABEL_ROADMAP }}`; + + /* + Bugs + */ + + const words = [ "bug", "broke", "issue", "fail" ]; + const bTriggerWordInTitle = words.some( s => s.includes( iss_title_lc ) || iss_title_lc.includes( s ) ); + + /* + Find regex based phrases + + Regex: + https://regex101.com/r/Z99Gnq/2 + */ + + const findWordList = /^\b(?:I?\s*have\s*(?:a|an)\s*(?:issue|problem|bug))|(?:will\s*not\s*work)|(?:it\s*is\s*(?:broken|broke|stuck))|(?:found\s*(?:an?|the)\s*(?:bug|issue))|(?:can\s*I\s*fix\s*the\s*(?:bug|issue))|(?:(?:does not|doesn'?t|don'?t|won'?t|can'?t|can\s?not|will\s*not)\s*(?:work|load|function))|(?:it\s*(?:will\s?not|won'?t|can\s?not|can'?t))\s*(?:get|find)\s*the\s*(?:website|site|webpage|page)|(?:the\s*(?:window|frame)\s*is\s*(?:blank|white|empty|missing))\b$/igm; + const bFoundMatchTitle = Boolean( findWordList.test( iss_title ) ); + const bFoundMatchBody = Boolean( findWordList.test( iss_body ) ); + + /* + Do not change a title if the item starts with a PR: # + + Regex: + https://regex101.com/r/JOrqbN/1 + */ + + const bug_findPRTitle = /^PR\s?#?(?:[0-9]*:)/igm; + const bug_bFoundPRTitle = Boolean( bug_findPRTitle.test( iss_title ) ); + + console.log( "Title Lowercase ............... " + iss_title_lc ) + console.log( "Startswith " + bug_tag.toLowerCase( ) + "................ " + iss_title_lc.startsWith( bug_tag.toLowerCase( ) ) ) + console.log( "Title Includes Keyword ........ " + bTriggerWordInTitle ) + console.log( "Title Includes Regex .......... " + bFoundMatchTitle ) + console.log( "Body Includes Regex ........... " + bFoundMatchBody ) + console.log( "\n" ) + + /* + - Check if issue title matches the issue label "Bug:" + - Check if title contains word in words + */ + + if ( iss_title_lc.startsWith( bug_tag.toLowerCase( ) ) || bTriggerWordInTitle || bFoundMatchTitle || bFoundMatchBody ) + { + + console.log( "⚠️ " + bug_tag + " ---------------------------------------" ) + console.log( "Already starts with " + bug_tag + " ......... " + iss_title_lc.startsWith( bug_tag.toLowerCase( ) ) ) + console.log( "Already starts with " + feat_tag + " ..... " + iss_title_lc.startsWith( feat_tag.toLowerCase( ) ) ) + console.log( "Already starts with " + urgn_tag + " ...... " + iss_title_lc.startsWith( urgn_tag.toLowerCase( ) ) ) + console.log( "Already starts with " + road_tag + " ..... " + iss_title_lc.startsWith( road_tag.toLowerCase( ) ) ) + + add_labels.push( `${ bug_lbl }` ); + + console.log( `Adding Tag ....................... ${ bug_lbl }` ) + console.log( "\n" ) + + if ( iss_author === `${{ env.BOT_NAME_DEPENDABOT }}` ) + core.info( `Skipping: Detected ${ iss_author }` ) + + // Rename title to contain Bug: + // Make sure issue / pr title doesnt already contain a beginning title tag + + if ( iss_author !== `${{ env.BOT_NAME_DEPENDABOT }}` && !bug_bFoundPRTitle && !iss_title_lc.startsWith( bug_tag.toLowerCase( ) ) && !iss_title_lc.startsWith( feat_tag.toLowerCase( ) ) && !iss_title_lc.startsWith( urgn_tag.toLowerCase( ) ) && !iss_title_lc.startsWith( road_tag.toLowerCase( ) ) ) + { + console.log( "Renaming Title" ) + console.log( `Old Title: .................. ${ iss_title }` ) + + const title = context.payload.issue.title + let title_new = title.replace( /^\s?bug\s*(.*?)\b/gi, '' ); + title_new = title.replace( /^\s?fail\s*(.*?)\b/gi, '' ); + title_new = title.replace( /^\s?issue\s*(.*?)\b/gi, '' ); + iss_title = `${ bug_tag } ${ title_new }`; + } + + console.log( `New Title: ...................... ${ iss_title }` ) + + await github.rest.issues.update( + { + owner: context.repo.owner, repo: context.repo.repo, issue_number: context.issue.number, + title: `${ iss_title }`, labels: add_labels + } ); + } + + core.setOutput( 'issue_title', iss_title ) + console.log( "\n\n" ) + + # # + # Labels > Features + # + # Title of issue is carried over from the previous step. + # # + + - name: >- + 🏷️ ${{ env.PREFIX_FEATURE }} › Assignment + uses: actions/github-script@v7 + id: task_issues_features + with: + github-token: ${{ secrets.ADMINSERV_TOKEN_CL }} + script: | + + const issueLabels = await github.rest.issues.listLabelsOnIssue( + { + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number + }); + + let add_labels = issueLabels.data.map( label => label.name ); + + let iss_title = `${{ steps.task_issues_bugs.outputs.issue_title }}` || `${ context.payload.issue.title }`; + let iss_body = `${ context.payload.issue.body }`; + + const iss_title_lc = iss_title.toLowerCase( ); + + console.log( "Feat Title .................... " + iss_title ) + console.log( "Feat Output ................... " + `${{ steps.task_issues_bugs.outputs.issue_title }}` ) + console.log( "Feat Payload .................. " + `${ context.payload.issue.title }` ) + + /* + Tags + */ + + const bug_tag = `${{ env.PREFIX_BUG }}:`; + const bug_lbl = `${{ env.LABEL_BUG }}`; + const feat_tag = `${{ env.PREFIX_FEATURE }}:`; + const feat_lbl = `${{ env.LABEL_FEATURE }}`; + const urgn_tag = `${{ env.PREFIX_URGENT }}:`; + const urgn_lbl = `${{ env.LABEL_URGENT }}`; + const road_tag = `${{ env.PREFIX_ROADMAP }}:`; + const road_lbl = `${{ env.LABEL_ROADMAP }}`; + + /* + Features + */ + + const words = [ "feature", "request", "add support" ]; + const bTriggerWordInTitle = words.some( s => s.includes( iss_title_lc ) || iss_title_lc.includes( s ) ); + + /* + Find regex based phrases + + Regex: + https://regex101.com/r/fR1Hm6/1 + */ + + const findWordList = /^(?:(?:request|include|see)\s*(?:an?|the?)\s*(?:feature|addon|addition|plugin))|(?:(?:add|see|get)\s*support\s*(?:for|with|of))|(?:can\s*we\s*get\s*(?:the|a)\s*(?:ability|feature))|(?:💡 Feature:)$/igm; + const bFoundMatchTitle = Boolean( findWordList.test( iss_title ) ); + const bFoundMatchBody = Boolean( findWordList.test( iss_body ) ); + + /* + Do not change a title if the item starts with a PR: # + + Regex: + https://regex101.com/r/JOrqbN/1 + */ + + const feat_findPRTitle = /^PR\s?#?(?:[0-9]*:)/igm; + const feat_bFoundPRTitle = Boolean( feat_findPRTitle.test( iss_title ) ); + + console.log( "Title Lowercase ............... " + iss_title_lc ) + console.log( "Startswith " + feat_tag.toLowerCase( ) + "............ " + iss_title_lc.startsWith( feat_tag.toLowerCase( ) ) ) + console.log( "Title Includes Keyword ........ " + bTriggerWordInTitle ) + console.log( "Title Includes Regex .......... " + bFoundMatchTitle ) + console.log( "Body Includes Regex ........... " + bFoundMatchBody ) + console.log( "\n" ) + + /* + - Check if issue title matches the issue label "Feature:" + - Check if title contains word in words + */ + + // change TAG per category + if ( iss_title_lc.startsWith( feat_tag.toLowerCase( ) ) || bTriggerWordInTitle || bFoundMatchTitle || bFoundMatchBody ) + { + + console.log( "⚠️ " + feat_tag + " ---------------------------------------" ) + console.log( "Already starts with " + bug_tag + " ......... " + iss_title_lc.startsWith( bug_tag.toLowerCase( ) ) ) + console.log( "Already starts with " + feat_tag + " ..... " + iss_title_lc.startsWith( feat_tag.toLowerCase( ) ) ) + console.log( "Already starts with " + urgn_tag + " ...... " + iss_title_lc.startsWith( urgn_tag.toLowerCase( ) ) ) + console.log( "Already starts with " + road_tag + " ..... " + iss_title_lc.startsWith( road_tag.toLowerCase( ) ) ) + + // change LBL per category + add_labels.push( `${ feat_lbl }` ); + + console.log( `Adding Tag ....................... ${ feat_lbl }` ) + console.log( "\n" ) + + if ( iss_author === `${{ env.BOT_NAME_DEPENDABOT }}` ) + core.info( `Skipping: Detected ${ iss_author }` ) + + // Rename title to contain Feature: + // Make sure issue / pr title doesnt already contain a beginning title tag + + if ( iss_author !== `${{ env.BOT_NAME_DEPENDABOT }}` && !feat_bFoundPRTitle && !iss_title_lc.startsWith( bug_tag.toLowerCase( ) ) && !iss_title_lc.startsWith( feat_tag.toLowerCase( ) ) && !iss_title_lc.startsWith( urgn_tag.toLowerCase( ) ) && !iss_title_lc.startsWith( road_tag.toLowerCase( ) ) ) + { + console.log( "Renaming Title" ) + console.log( `Old Title: .................. ${ iss_title }` ) + + const title = context.payload.issue.title + let title_new = title.replace( /^\s?feature\s*(.*?)\b/gi, '' ); + title_new = title.replace( /^\s?request\s*(.*?)\b/gi, '' ); + title_new = title.replace( /^\s?add(.*?)\s?feature\s*(.*?)\b/gi, '' ); + title_new = title.replace( /^\s?add(.*?)\s?support\s*(.*?)\b/gi, '' ); + iss_title = `${ feat_tag } ${ title_new }`; // change TAG per category + } + + console.log( `New Title: ...................... ${ iss_title }` ) + + await github.rest.issues.update( + { + owner: context.repo.owner, repo: context.repo.repo, issue_number: context.issue.number, + title: `${ iss_title }`, labels: add_labels + } ); + } + + core.setOutput( 'issue_title', iss_title ) + console.log( "\n\n" ) + + # # + # Labels > Urgent + # + # Title of issue is carried over from the previous step. + # # + + - name: >- + 🏷️ ${{ env.PREFIX_URGENT }} › Assignment + uses: actions/github-script@v7 + id: task_issues_urgent + with: + github-token: ${{ secrets.ADMINSERV_TOKEN_CL }} + script: | + const issueLabels = await github.rest.issues.listLabelsOnIssue( + { + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number + }); + + let add_labels = issueLabels.data.map( label => label.name ); + + let iss_title = `${{ steps.task_issues_features.outputs.issue_title }}` || `${ context.payload.issue.title }`; + let iss_body = `${ context.payload.issue.body }`; + + const iss_title_lc = iss_title.toLowerCase( ); + + console.log( "Urgn Title .................... " + iss_title ) + console.log( "Urgn Output ................... " + `${{ steps.task_issues_features.outputs.issue_title }}` ) + console.log( "Urgn Payload .................. " + `${ context.payload.issue.title }` ) + + /* + Tags + */ + + const bug_tag = `${{ env.PREFIX_BUG }}:`; + const bug_lbl = `${{ env.LABEL_BUG }}`; + const feat_tag = `${{ env.PREFIX_FEATURE }}:`; + const feat_lbl = `${{ env.LABEL_FEATURE }}`; + const urgn_tag = `${{ env.PREFIX_URGENT }}:`; + const urgn_lbl = `${{ env.LABEL_URGENT }}`; + const road_tag = `${{ env.PREFIX_ROADMAP }}:`; + const road_lbl = `${{ env.LABEL_ROADMAP }}`; + + /* + Urgent + */ + + const words = [ "urgent", "urgency", "emergency", "important", "critical" ]; + const bTriggerWordInTitle = words.some( s => s.includes( iss_title_lc ) || iss_title_lc.includes( s ) ); + + /* + Find regex based phrases + + Regex: + https://regex101.com/r/eE9tJX/2 + */ + + const findWordList = /(?:(?:this)?is\s*a?n?\s*?(?:emergency|urgent|important|vital|acute|crucial|grave|pressing|serious|top.?priority|high.?priority))|(?:reply|respond|answer|write|address)\s*(?:immediate|quick|asap|urgent|now|fast|(?:as)?\s*(?:soon|quick|immediate|fast))(?:ly)?|(?:need\s*(?:help|support|fixed|answer|reply|response)!)|(?:emergency|critical|urgen(?:t|cy)|high.?priority)/igm; + const bFoundMatchTitle = Boolean( findWordList.test( iss_title ) ); + const bFoundMatchBody = Boolean( findWordList.test( iss_body ) ); + + /* + Do not change a title if the item starts with a PR: # + + Regex: + https://regex101.com/r/JOrqbN/1 + */ + + const urgn_findPRTitle = /^PR\s?#?(?:[0-9]*:)/igm; + const urgn_bFoundPRTitle = Boolean( urgn_findPRTitle.test( iss_title ) ); + + console.log( "Title Lowercase ............... " + iss_title_lc ) + console.log( "Startswith " + urgn_tag.toLowerCase( ) + "............. " + iss_title_lc.startsWith( urgn_tag.toLowerCase( ) ) ) + console.log( "Title Includes Keyword ........ " + bTriggerWordInTitle ) + console.log( "Title Includes Regex .......... " + bFoundMatchTitle ) + console.log( "Body Includes Regex ........... " + bFoundMatchBody ) + console.log( "\n" ) + + /* + - Check if issue title matches the issue label "Urgent:" + - Check if title contains word in words + */ + + // change TAG per category + if ( iss_title_lc.startsWith( urgn_tag.toLowerCase( ) ) || bTriggerWordInTitle || bFoundMatchTitle || bFoundMatchBody ) + { + + console.log( "⚠️ " + urgn_tag + " ---------------------------------------" ) + console.log( "Already starts with " + bug_tag + " ......... " + iss_title_lc.startsWith( bug_tag.toLowerCase( ) ) ) + console.log( "Already starts with " + feat_tag + " ..... " + iss_title_lc.startsWith( feat_tag.toLowerCase( ) ) ) + console.log( "Already starts with " + urgn_tag + " ...... " + iss_title_lc.startsWith( urgn_tag.toLowerCase( ) ) ) + console.log( "Already starts with " + road_tag + " ..... " + iss_title_lc.startsWith( road_tag.toLowerCase( ) ) ) + + // change LBL per category + add_labels.push( `${ urgn_lbl }` ); + + console.log( `Adding Tag ....................... ${ urgn_lbl }` ) + console.log( "\n" ) + + if ( iss_author === `${{ env.BOT_NAME_DEPENDABOT }}` ) + core.info( `Skipping: Detected ${ iss_author }` ) + + // Rename title to contain Urgent: + // Make sure issue / pr title doesnt already contain a beginning title tag + + if ( iss_author !== `${{ env.BOT_NAME_DEPENDABOT }}` && !urgn_bFoundPRTitle && !iss_title_lc.startsWith( bug_tag.toLowerCase( ) ) && !iss_title_lc.startsWith( feat_tag.toLowerCase( ) ) && !iss_title_lc.startsWith( urgn_tag.toLowerCase( ) ) && !iss_title_lc.startsWith( road_tag.toLowerCase( ) ) ) + { + console.log( "Renaming Title" ) + console.log( `Old Title: .................. ${ iss_title }` ) + + const title = context.payload.issue.title + let title_new = title.replace( /^\s?emergency\s*(.*?)\b/gi, '' ); + title_new = title.replace( /^\s?urgent\s*(.*?)\b/gi, '' ); + title_new = title.replace( /^\s?urgency\s*(.*?)\b/gi, '' ); + title_new = title.replace( /^\s?important\s*(.*?)\b/gi, '' ); + title_new = title.replace( /^\s?critical\s*(.*?)\b/gi, '' ); + iss_title = `${ urgn_tag } ${ title_new }`; // change TAG per category + } + + console.log( `New Title: ...................... ${ iss_title }` ) + + await github.rest.issues.update( + { + owner: context.repo.owner, repo: context.repo.repo, issue_number: context.issue.number, + title: `${ iss_title }`, labels: add_labels + } ); + } + + core.setOutput( 'issue_title', iss_title ) + console.log( "\n\n" ) + + # # + # Labels > Roadmap + # + # Title of issue is carried over from the previous step. + # # + + - name: >- + 🏷️ ${{ env.PREFIX_ROADMAP }} › Assignment + uses: actions/github-script@v7 + id: task_issues_roadmap + with: + github-token: ${{ secrets.ADMINSERV_TOKEN_CL }} + script: | + const issueLabels = await github.rest.issues.listLabelsOnIssue( + { + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number + }); + + let add_labels = issueLabels.data.map( label => label.name ); + + let iss_title = `${{ steps.task_issues_urgent.outputs.issue_title }}` || `${ context.payload.issue.title }`; + let iss_body = `${ context.payload.issue.body }`; + + const iss_title_lc = iss_title.toLowerCase( ); + + console.log( "Road Title .................... " + iss_title ) + console.log( "Road Output ................... " + `${{ steps.task_issues_urgent.outputs.issue_title }}` ) + console.log( "Road Payload .................. " + `${ context.payload.issue.title }` ) + + /* + Tags + */ + + const bug_tag = `${{ env.PREFIX_BUG }}:`; + const bug_lbl = `${{ env.LABEL_BUG }}`; + const feat_tag = `${{ env.PREFIX_FEATURE }}:`; + const feat_lbl = `${{ env.LABEL_FEATURE }}`; + const urgn_tag = `${{ env.PREFIX_URGENT }}:`; + const urgn_lbl = `${{ env.LABEL_URGENT }}`; + const road_tag = `${{ env.PREFIX_ROADMAP }}:`; + const road_lbl = `${{ env.LABEL_ROADMAP }}`; + + /* + Roadmap + */ + + const words = [ "roadmap", "road map", "planned" ]; + const bTriggerWordInTitle = words.some( s => s.includes( iss_title_lc ) || iss_title_lc.includes( s ) ); + + /* + Find regex based phrases + Roadmap requires headers #Summary and #Proposal | #Objective + + Regex: + https://regex101.com/r/ucajBZ/1 + */ + + const findWordList = /#\s*Summary[\S\s]+#\s*(?:Proposal|Objective)[^\]]+/igm; + const bFoundMatchTitle = Boolean( findWordList.test( iss_title ) ); + const bFoundMatchBody = Boolean( findWordList.test( iss_body ) ); + + /* + Do not change a title if the item starts with a PR: # + + Regex: + https://regex101.com/r/JOrqbN/1 + */ + + const road_findPRTitle = /^PR\s?#?(?:[0-9]*:)/igm; + const road_bFoundPRTitle = Boolean( road_findPRTitle.test( iss_title ) ); + + console.log( "Title Lowercase ............... " + iss_title_lc ) + console.log( "Startswith " + road_tag.toLowerCase( ) + "............ " + iss_title_lc.startsWith( road_tag.toLowerCase( ) ) ) + console.log( "Title Includes Keyword ........ " + bTriggerWordInTitle ) + console.log( "Title Includes Regex .......... " + bFoundMatchTitle ) + console.log( "Body Includes Regex ........... " + bFoundMatchBody ) + console.log( "\n" ) + + /* + - Check if issue title matches the issue label "Roadmap:" + - Check if title contains word in words + */ + + // change TAG per category + if ( iss_title_lc.startsWith( road_tag.toLowerCase( ) ) || bTriggerWordInTitle || bFoundMatchTitle || bFoundMatchBody ) + { + + console.log( "⚠️ " + road_tag + " ---------------------------------------" ) + console.log( "Already starts with " + bug_tag + " ...... " + iss_title_lc.startsWith( bug_tag.toLowerCase( ) ) ) + console.log( "Already starts with " + feat_tag + " .. " + iss_title_lc.startsWith( feat_tag.toLowerCase( ) ) ) + console.log( "Already starts with " + urgn_tag + " ... " + iss_title_lc.startsWith( urgn_tag.toLowerCase( ) ) ) + console.log( "Already starts with " + road_tag + " .. " + iss_title_lc.startsWith( road_tag.toLowerCase( ) ) ) + + // change LBL per category + add_labels.push( `${ road_lbl }` ); + + console.log( `Adding Tag .................... ${ road_lbl }` ) + console.log( "\n" ) + + if ( iss_author === `${{ env.BOT_NAME_DEPENDABOT }}` ) + core.info( `Skipping: Detected ${ iss_author }` ) + + // Rename title to contain Roadmap: + // Make sure issue / pr title doesnt already contain a beginning title tag + + if ( iss_author !== `${{ env.BOT_NAME_DEPENDABOT }}` && !road_bFoundPRTitle && !iss_title_lc.startsWith( bug_tag.toLowerCase( ) ) && !iss_title_lc.startsWith( feat_tag.toLowerCase( ) ) && !iss_title_lc.startsWith( urgn_tag.toLowerCase( ) ) && !iss_title_lc.startsWith( road_tag.toLowerCase( ) ) ) + { + console.log( "Renaming Title" ) + console.log( `Old Title: .................. ${ iss_title }` ) + + const title = context.payload.issue.title + let title_new = title.replace( /^\s?broad(.*?)\s?map\s*(.*?)\b/gi, '' ); + title_new = title.replace( /^\s?planned\s*(.*?)\b/gi, '' ); + title_new = title.replace( /^\s?broadmap\s*(.*?)\b/gi, '' ); + iss_title = `${ road_tag } ${ title_new }`; // change TAG per category + } + + console.log( `New Title: .................... ${ iss_title }` ) + + await github.rest.issues.update( + { + owner: context.repo.owner, repo: context.repo.repo, issue_number: context.issue.number, + title: `${ iss_title }`, labels: add_labels + } ); + } + + core.setOutput( 'issue_title', iss_title ) + console.log( "\n\n" ) + + # # + # Job > Phrase Search + # + # Checks a message for certain keywords and then responds to the user as a reply / comment + # # + + job-phrase-search: + name: >- + 🏷️ Labels › Phrase Search + needs: + - job-labels-create + runs-on: ubuntu-latest + permissions: + contents: 'read' + id-token: 'write' + issues: 'write' + steps: + + # # + # [ Search Phrase ] Checkout + # # + + - name: >- + ☑️ Prepare + id: issues-labels-check-checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + # # + # [ Search Phrase ] Search + # # + + - name: >- + 👄 Search Phrases + uses: actions/github-script@v7 + with: + github-token: ${{ secrets.ADMINSERV_TOKEN_CL }} + script: | + const fs = require( 'fs' ); + const iss_title = `${ context.payload.issue.title }`; + const iss_body = `${ context.payload.issue.body }`; + let message = [ "\n
\n" ] + let bHasMessage = false + + /********************************************* + Keyword > Help + **********************************************/ + + let HE_message = + ` + 💡 It appears you might need help, please check the resources below for documentation that might assist with your issue: + - [Documentation](${{github.event.repository.url}}) + + --- + + I am a bot reaching out to you with an automated response. If the above info doesn't apply to you, please ignore it. + `; + + /* + found searched word "for help" + append / prepare message for bot to send + */ + + const HEfindWordList = /^\b(?:have\s*(?:a|some)?\s*question*s?)|(?:can\s*you\s*(?:tell|help)\s*me)|(?:need\s*(?:some)?\s*(?:help|assistance|guidance))|(?:how\s*can\s*I\s*find)|(?:point\s*me\s*in\s*the\s*direction)|(?:where\s*can\s*I\s*find)|(?:where\s*(?:\N*)\s*(?:\N*)\s*find)|(?:please\s*help)|(?:where\s*\N*\s*(?:located|at))|(?:documentation)\b$/igm; + const HEbFoundMatchTitle = Boolean( HEfindWordList.test( iss_title ) ); + const HEbFoundMatchBody = Boolean( HEfindWordList.test( iss_body ) ); + + if ( HEbFoundMatchTitle || HEbFoundMatchBody ) + { + message.push ( HE_message ); + bHasMessage = true; + } + + /* + Bot has message to send + */ + + if ( bHasMessage == true ) + { + await github.rest.issues.createComment( + { + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: message.join('\n'), + } ); + } + + # # + # Job > Add Assignees + # # + + job-assign-assignees: + name: >- + ✍️ Issue › Assignees + runs-on: ubuntu-latest + needs: [ job-assign-labels ] + if: | + always() + && contains( needs.*.result, 'success' ) + && !contains( needs.*.result, 'failure' ) + permissions: + contents: write + steps: + + # # + # [ Assignees] Assign + # # + + - name: >- + ✍️ Set Assignees + uses: actions/github-script@v7 + with: + github-token: ${{ secrets.ADMINSERV_TOKEN_CL }} + script: | + const assignees = [ `${{ github.repository_owner }}` ]; + + if ( assignees.length > 0 ) + { + try + { + await github.rest.issues.addAssignees( + { + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + assignees + }); + } + catch ( error ) + { + core.setFailed( error.message ); + } + } diff --git a/.github/workflows/issues-scan.yml b/.github/workflows/issues-scan.yml new file mode 100644 index 0000000..2121a3c --- /dev/null +++ b/.github/workflows/issues-scan.yml @@ -0,0 +1,788 @@ +# # +# @type github workflow +# @desc pull request autoscan +# scans all of the files related to a particular pull request +# if the code in the files being submitted contains code that is forbidden, +# a report is generated and posted as a comment in the PR. +# @author Aetherinox +# @url https://github.com/Aetherinox +# # + +name: "🎫 Issues › Scan" +run-name: "🎫 Issues › Scan" + +# # +# triggers +# # + +on: + pull_request_target: + branches: + - main + - master + +# # +# environment variables +# # + +env: + LABEL_CHECK_STATUS_FAILED: AC › Failed + LABEL_CHECK_REVIEW_READY: AC › Passed + LABEL_CHECK_CHANGES_REQ: AC › Changes Required + LABEL_CHECK_REVIEW_REQ: AC › Review Required + LABEL_CHECK_REBASE_REQ: AC › Needs Rebase + LABEL_CHECK_SECURITY_ERR: AC › Security Warning + LABEL_CHECK_STATUS_CHGMADE: AC › Changes Made + LABEL_CHECK_SCAN_SKIPPED: AC › Skipped Scan + LABEL_TYPE_PR: Type ◦ Pull Request + LABEL_TYPE_DEPENDENCY: Type ◦ Dependency + LABEL_TYPE_GITACTION: Type ◦ Git Action + + BOT_NAME_1: AdminServ + BOT_NAME_2: AdminServX + BOT_NAME_3: EuropaServ + BOT_NAME_DEPENDABOT: dependabot[bot] + + LABELS_JSON: | + [ + { "name": "AC › Changes Made", "color": "8F1784", "description": "Requested changes have been made and are pending a re-scan" }, + { "name": "AC › Changes Required", "color": "8F1784", "description": "Requires changes to be made to the package before being accepted" }, + { "name": "AC › Failed", "color": "a61f2d", "description": "Autocheck failed to run through a complete cycle, requires investigation" }, + { "name": "AC › Needs Rebase", "color": "8F1784", "description": "Due to the permissions on the requesting repo, this pull request must be rebased by the author" }, + { "name": "AC › Passed", "color": "146b4a", "description": "Ready to be reviewed" }, + { "name": "AC › Review Required", "color": "8F1784", "description": "PR needs to be reviewed by another person, after the requested changes have been made" }, + { "name": "AC › Security Warning", "color": "761620", "description": "Does not conform to developer policies, or includes potentially dangerous code" }, + { "name": "AC › Skipped Scan", "color": "8F1784", "description": "Author has skipped code scan" }, + { "name": "Status 𐄂 Duplicate", "color": "75536b", "description": "Issue or pull request already exists" }, + { "name": "Status 𐄂 Accepted", "color": "2e7539", "description": "This pull request has been accepted" }, + { "name": "Status 𐄂 Autoclosed", "color": "3E0915", "description": "Originally stale and was autoclosed for no activity" }, + { "name": "Status 𐄂 Denied", "color": "ba4058", "description": "Pull request has been denied" }, + { "name": "Status 𐄂 Locked", "color": "550F45", "description": "Automatically locked by AdminServ for a prolonged period of inactivity" }, + { "name": "Status 𐄂 Need Info", "color": "2E3C4C", "description": "Not enough information to resolve" }, + { "name": "Status 𐄂 No Action", "color": "030406", "description": "Closed without any action being taken" }, + { "name": "Status 𐄂 Pending", "color": "984b12", "description": "Pending pull request" }, + { "name": "Status 𐄂 Released", "color": "1b6626", "description": "Issues or PR has been implemented and is now live" }, + { "name": "Status 𐄂 Reopened", "color": "8a6f14", "description": "A previously closed PR which has been re-opened" }, + { "name": "Status 𐄂 Review", "color": "9e1451", "description": "Currently pending review" }, + { "name": "Status 𐄂 Stale", "color": "928282", "description": "Has not had any activity in over 30 days" }, + { "name": "Type ◦ Bug", "color": "9a2c2c", "description": "Something isn't working" }, + { "name": "Type ◦ Dependency", "color": "243759", "description": "Item is associated to dependency" }, + { "name": "Type ◦ Docs", "color": "0e588d", "description": "Improvements or modifications to docs" }, + { "name": "Type ◦ Feature", "color": "3c4e93", "description": "Feature request" }, + { "name": "Type ◦ Git Action", "color": "030406", "description": "GitHub Action / workflow" }, + { "name": "Type ◦ Pull Request", "color": "8F1784", "description": "Normal pull request" }, + { "name": "Type ◦ Roadmap", "color": "8F1784", "description": "Feature or bug currently planned for implementation" }, + { "name": "Type ◦ Internal", "color": "A51994", "description": "Assigned items are for internal developer use" }, + { "name": "Build ◦ Desktop", "color": "c7ca4a", "description": "Specific to desktop" }, + { "name": "Build ◦ Linux", "color": "c7ca4a", "description": "Specific to Linux" }, + { "name": "Build ◦ MacOS", "color": "c7ca4a", "description": "Specific to MacOS" }, + { "name": "Build ◦ Mobile", "color": "c7ca4a", "description": "Specific to mobile" }, + { "name": "Build ◦ Web", "color": "c7ca4a", "description": "Specific to web" }, + { "name": "Build ◦ Windows", "color": "c7ca4a", "description": "Specific to Windows" }, + { "name": "› API", "color": "F99B50", "description": "Plugin API, CLI, browser JS API" }, + { "name": "› Auto-type", "color": "9141E0", "description": "Auto-type functionality in desktop apps" }, + { "name": "› Browser", "color": "9141E0", "description": "Browser plugins and passing data to <=> from app" }, + { "name": "› Customization", "color": "E3F0FC", "description": "Customizations: plugins, themes, configs" }, + { "name": "› Design", "color": "FA70DE", "description": "Design related queries" }, + { "name": "› Dist", "color": "FA70DE", "description": "Installers and other forms of software distribution" }, + { "name": "› Enterprise", "color": "11447a", "description": "Issues about collaboration, administration, and so on" }, + { "name": "› Hardware", "color": "5a7503", "description": "YubiKey, other tokens, biometrics" }, + { "name": "› Import/Export", "color": "F5FFCC", "description": "Import from and export to different file formats" }, + { "name": "› Improvement", "color": "185c98", "description": "Enhance an existing feature" }, + { "name": "› Performance", "color": "006b75", "description": "Web and desktop performance issues" }, + { "name": "› Plugin Request", "color": "FCE9CA", "description": "Requested changes should be implemented as a plugin" }, + { "name": "› Security", "color": "F75D39", "description": "Security issues" }, + { "name": "› Self-Hosting", "color": "fad8c7", "description": "Self-hosting installations and configs" }, + { "name": "› Storage", "color": "5319e7", "description": "Storage providers: Dropbox, Google, WebDAV, etc." }, + { "name": "› Updater", "color": "1BADDE", "description": "Auto-updater issues" }, + { "name": "› UX", "color": "1BADDE", "description": "UX and usability" }, + { "name": "› Website", "color": "fef2c0", "description": "Website related issues" }, + { "name": "⚠ Urgent", "color": "a8740e", "description": "Requires urgent attention" }, + { "name": "⚠ Announcement", "color": "DB4712", "description": "Announcements" }, + { "name": "📰 Progress Report", "color": "392297", "description": "Development updates" }, + { "name": "📦 Release", "color": "277542", "description": "Release announcements" }, + { "name": "✔️ Poll", "color": "972255", "description": "Community polls" }, + { "name": "❔ Question", "color": "FFFFFF", "description": "All questions" } + ] + +# # +# jobs +# # + +jobs: + + # # + # Job [ Autoscan ] + # # + + pr-autoscan: + runs-on: ubuntu-latest + permissions: + contents: read + actions: read + issues: write + pull-requests: read + steps: + + # # + # action needed if using 'pull_request' and 'issue_comment' + # to get the pull request, you would normally use ${{ github.event.number }} + # however this isnt available for 'issue_comment' + # # + + - name: >- + 🏷️ Verify Existing Labels + id: task_autocheck_labels_verify + uses: actions/github-script@v7 + with: + github-token: ${{ secrets.ADMINSERV_TOKEN_CL || github.token }} + script: | + const labels = JSON.parse( process.env.LABELS_JSON ); + for ( const label of labels ) + { + try + { + await github.rest.issues.createLabel( + { + owner: context.repo.owner, + repo: context.repo.repo, + name: label.name, + description: label.description || '', + color: label.color + }); + } + catch ( err ) + { + if ( err.status === 422 ) + { + console.log( `Label '${label.name}' already exists. Skipping.` ); + } + else + { + console.error( `Error creating label '${label.name}': ${err}` ); + } + } + } + + # # + # set issue number + # # + + - name: >- + #️⃣ Issue number › Set + uses: actions/github-script@v7 + id: task_autocheck_issue_num_set + with: + github-token: ${{ secrets.ADMINSERV_TOKEN_CL || github.token }} + script: | + if ( context.issue.number ) + { + // Return issue number if present + return context.issue.number; + } + else + { + // Otherwise return issue number from commit + return ( + await github.rest.repos.listPullRequestsAssociatedWithCommit( + { + commit_sha: context.sha, + owner: context.repo.owner, + repo: context.repo.repo, + }) + ).data[ 0 ].number; + } + result-encoding: string + + # # + # print issue number + # # + + - name: >- + #️⃣ Issue number › Print + id: task_autocheck_issue_num_get + run: | + echo '${{ steps.task_autocheck_issue_num_set.outputs.result }}' + + # # + # checkout + # # + + - name: >- + ☑️ Checkout + id: task_autoscan_checkout + uses: actions/checkout@v4 + if: | + ( github.event_name == 'pull_request_target' ) || ( github.event_name == 'pull_request' ) || ( github.event_name == 'issue_comment' && contains( github.event.comment.html_url, '/pull/' ) && contains( github.event.comment.body, '/rescan' ) ) + with: + fetch-depth: 0 + ref: "refs/pull/${{ steps.task_autocheck_issue_num_set.outputs.result }}/merge" + + # # + # nodejs + # # + + - name: >- + ⚙️ Setup Node + id: task_autocheck_nodejs + uses: actions/setup-node@v4 + + # # + # get list of changed files + # + # Effortlessly track all changed files and directories relative to a target branch, + # the current branch (preceding commit or the last remote commit), multiple branches, + # or custom commits returning relative paths from the project root using this + # GitHub action. + # # + + - name: >- + 📄 Get changed files + id: task_autocheck_changed_files_get + uses: tj-actions/changed-files@v45 + with: + separator: "," + + # # + # list of changed files + # # + + - name: >- + 📄 List all added files + id: task_autocheck_added_files_get + run: | + for file in ${CHANGED_FILES}; do + echo "$file was changed" + done + env: + ADDED_FILES: ${{ steps.task_autocheck_changed_files_get.outputs.added_files }} + MODIFIED_FILES: ${{ steps.task_autocheck_changed_files_get.outputs.modified_files }} + CHANGED_FILES: ${{ steps.task_autocheck_changed_files_get.outputs.all_changed_files }} + COUNT_ADDED: ${{ steps.task_autocheck_changed_files_get.outputs.added_files_count }} + COUNT_MODIFIED: ${{ steps.task_autocheck_changed_files_get.outputs.modified_files_count }} + COUNT_DELETED: ${{ steps.task_autocheck_changed_files_get.outputs.deleted_files_count }} + COUNT_RENAMED: ${{ steps.task_autocheck_changed_files_get.outputs.renamed_files_count }} + COUNT_COPIED: ${{ steps.task_autocheck_changed_files_get.outputs.copied_files_count }} + + # # + # List directories + # # + + - name: >- + 📂 List Directories + id: task_autocheck_dirs_list + run: | + ls + + # # + # Run autocheck + # # + + - name: >- + ☑️ Run Autocheck + id: task_autocheck_run + uses: actions/github-script@v7 + with: + github-token: ${{ secrets.ADMINSERV_TOKEN_CL || github.token }} + script: | + const fs = require( 'fs' ); + const escape_html = ( unsafe ) => unsafe.replace( /&/g, '&' ).replace( //g, '>' ).replace( /"/g, '"' ).replace( /'/g, ''' ); + const labels = []; + + const files_List = `${{ steps.task_autocheck_changed_files_get.outputs.all_changed_files }}` || '' + const files_Array = files_List.split(',') + const branch_ref = `${ context.payload.pull_request.head.ref }` + + let message = [ "\n
\n" ] + message.push ( "## Automatic Self-Check - #" + context.issue.number + "\n" ); + message.push ( `The details of our automated scan for your pull request are listed below. If our scan detected errors, they must be corrected before this pull request will be advanced to the review stage:\n` ); + message.push ( "\n
\n\n---\n\n
\n\n" ); + message.push ( "### About\nThis pull request includes the following information:" ); + + let bHasError = false; + let bHasWarning = false; + + let date = new Date( `${ context.payload.pull_request.created_at }` ); + date.toISOString( ) + + const actor = '${{ github.actor }}'; + + const dateTimeformat = ( date ) => + { + let month = date.getMonth( ) + 1; + month = month.toString( ).padStart( 2, '0' ); + let day = date.getDate( ).toString( ).padStart( 2, '0' ); + let year = date.getFullYear( ).toString( ).padStart( 2, '0' ); + + let hours = date.getHours(); + let minutes = date.getMinutes(); + let x = hours >= 12 ? 'PM' : 'AM'; + hours = hours % 12; + hours = hours ? hours : 12; + minutes = minutes.toString( ).padStart( 2, '0' ); + + let mergeTime = month + '.' + day + '.' + year + ' ' + hours + ':' + minutes + ' ' + x; + + return mergeTime; + } + + let date_created = dateTimeformat( date ) + " UTC"; + + /* + context.payload.pull_request.base.repo.owner.login + */ + + let md_table = + ` + | Category | Value | + | --- | --- | + | Title | [ ` + context.payload.pull_request.title + ` ](https://github.com/` + context.repo.owner + `/` + context.repo.repo + `/pull/` + context.payload.pull_request.number + `) | + | Created | [ ` + date_created + ` ](https://worldtimebuddy.com) | + | ID | ` + context.payload.pull_request.html_url + ` | + | Author | [ ` + context.payload.pull_request.user.login + ` ](https://github.com/` + context.repo.owner + `/) | + | Repo | [ ` + context.repo.repo + ` ](https://github.com/` + context.repo.owner + `/` + context.repo.repo + `) | + | Branch | [ ` + context.payload.pull_request.head.ref + `](https://github.com/` + context.repo.owner + `/` + context.repo.repo + `/tree/` + context.payload.pull_request.head.ref + `) ⇁ [ ` + context.payload.pull_request.base.ref + `](https://github.com/` + context.repo.owner + `/` + context.repo.repo + `/tree/` + context.payload.pull_request.base.ref + `) | + | Added Files | ${{ steps.task_autocheck_changed_files_get.outputs.added_files_count }} | + | Modified Files | ${{ steps.task_autocheck_changed_files_get.outputs.all_modified_files_count }} | + | Renamed Files | ${{ steps.task_autocheck_changed_files_get.outputs.renamed_files_count }} | + | Copied Files | ${{ steps.task_autocheck_changed_files_get.outputs.deleted_files_count }} | + | Deleted Files | ${{ steps.task_autocheck_changed_files_get.outputs.deleted_files_count }} | + `; + + message.push ( md_table ); + + let error_Generic = "\n" + + "- `MyPlugin`\n" + + "- `MyPluginSettings`\n" + + "- `SampleSettings`\n" + + "- `SampleSettingTab`\n" + + "- `SampleModal`\n" + + let warn_BadWords = "\n" + + "- `General`\n" + + "- `Settings`\n" + + /* + Loop files + */ + + const files_skipped = []; + + /* + List of files to skip check + Entries are CASE sensitive + For folders, append / at the end of the parent directory + */ + + const type_dependency = + [ + "dependabot/npm_and_yarn" + ]; + + const type_gitaction = + [ + "dependabot/github_actions" + ]; + + const files_skipList = + [ + ".github", + ".gitea", + ".gitignore", + "LICENSE", + ".md", + ".yml", + "plugins.json", + "package.json", + "package-lock.json", + "rollup.config.js", + "index.js", + "gistr.js", + "Docs/", + "tests/" + ]; + + for ( const file of files_Array ) + { + + const errors = []; + const addError = ( error ) => + { + errors.push ( `:x: ${error}` ); + console.log ( 'Found Issues: ' + error ); + + bHasError = true; + }; + + const warnings = []; + const addWarning = ( warning ) => + { + warnings.push ( `:warning: ${warning}` ); + console.log ( 'Found Warnings: ' + warning ); + + bHasWarning = true; + } + + /* + Regex Searches + */ + + const file_current = file; + const filesData = fs.readFileSync( file_current, 'utf8' ); + const bContainsStyle = /([A-Za-z]+\.style\.[A-Za-z]+)/gi.test( filesData ); + const bFuncFetch = /(fetch)\((.*)\)(\[([^\]]*)\])?/gim.test( filesData ); + const bVar = /^(?:var|)\s(\w+)\s*=\s*/gm.test( filesData ); + const bLookBehind = /\(\?<[=!].*?\)/gmi.test( filesData ); + const bMarkdownHtmlNode = /new\s+NodeHtmlMarkdown/gmi.test( filesData ); + const bAsTFile = /as\s+TFile/g.test( filesData ); + const bAsTFolder = /as\s+TFolder/g.test( filesData ); + const bAsAny = /\((.*? as Any\s*)\)/gi.test( filesData ); + const bInnerHTML = /^\s?.*[a-zA-Z0-9_]+\.innerHTML*\s?.*$/gm.test( filesData ); + const bOuterHTML = /^\s?.*[a-zA-Z0-9_]+\.outerHTML*\s?.*$/gm.test( filesData ); + // const bFuncConsoleLog = /(console.log)\((.*)\)(\[([^\]]*)\])?/gim.test( filesData ); + const bFuncSetTimeout = /(setTimeout)\((.*)\)(\[([^\]]*)\])?/gim.test( filesData ); + const bFuncFS_Chk1 = /(require)\s?\((\s?(?:'|")fs(?:'|"))\s?\)?/gim.test( filesData ); + const bFuncFS_Chk2 = /from\s+(?:'|")fs(?:'|")\s?/gim.test( filesData ); + const bFuncFS_ExistsSync = /(fs.existsSync)\((.*)\)(\[([^\]]*)\])?/gm.test( filesData ); + const bFuncFS_MkdirSync = /(fs.mkdirSync)\((.*)\)(\[([^\]]*)\])?/gm.test( filesData ); + const bFoundBadWord = /(?:'|").*(Settings|General).*(?:'|")?/gmi.test( filesData ); + const bContainsGeneric = /(?:^|(?<= ))(MyPlugin|MyPluginSettings|SampleSettings|SampleSettingTab|SampleModal|Sample Plugin|my-plugin)(?:(?= )|$)/gim.test( filesData ); + const check_depGetUnpinnedLeaf = "app.workspace.getUnpinnedLeaf" + + const bFileSkip = files_skipList.some( s => s.includes( file_current ) || file_current.includes( s ) ); + + if ( bFileSkip == true ) + { + files_skipped.push( file_current ); + continue; + } + + /* + Header + */ + + message.push ( "\n
\n\n---\n\n
\n" ); + message.push ( "### 📄 " + file_current + "\n" ); + message = message.concat( warnings ); + + /* + Skip File + + all contents in the array below will be skipped. + + E.g: any file which resides in the .github folder will be skipped. + any file which ends in .yml will be skipped. + */ + + /* + ( Deprecated ) app.workspace.getUnpinnedLeaf + + @usage : obsidian.md + */ + + /* + if ( filesData.toLowerCase( ).includes( check_depGetUnpinnedLeaf.toLowerCase( ) ) ) + { + addError( "This function is deprecated, use `this.app.workspace.getLeaf( false )` instead" ); + } + */ + + /* + Using inline style + */ + + if ( bContainsStyle == true ) + { + addError( "Avoid assigning `inline styles` via JavaScript or in HTML. Move these styles to CSS so that they are adaptable by themes and other plugins." ); + } + + /* + Using fetch + */ + + if ( bFuncFetch == true ) + { + addError( "Do not handle http data with `fetch( )`. Use the Obsidian API -> `requestUrl` method instead, which will make sure that network requests work on every platform." ); + } + + /* + Using var + */ + + if ( bVar == true ) + { + addError( "Change all instances of `var` to **const** or **let**. var has function-level scope, and leads to bugs." ); + } + + /* + Using lookbehind + */ + + if ( bLookBehind == true ) + { + addError( "Lookbehinds are not supported in iOS < 16.4" ); + } + + /* + Using HTML Node + */ + + if ( bMarkdownHtmlNode == true ) + { + addError( "Do not use `NodeHtmlMarkdown`. Use Obsidian API -> `htmlToMarkdown` instead." ); + } + + /* + As TFile + */ + + if ( bAsTFile == true ) + { + addError( "Do not cast `as TFile`, use `instanceof` instead to check if the item is actually a file / folder" ); + } + + /* + As TFolder + */ + + if ( bAsTFolder == true ) + { + addError( "Do not cast `as TFolder`, use `instanceof` instead to check if the item is actually a file / folder" ); + } + + /* + Casting to Any + */ + + if ( bAsAny == true ) + { + addError( "Do not cast to `Any`" ); + } + + /* + innerHTML + */ + + if ( bInnerHTML == true ) + { + addError( `Using \`innerHTML\` is a security risk.` ); + } + + /* + outerHTML + */ + + if ( bOuterHTML == true ) + { + addError( `Using \`outerHTML\` is a security risk.` ); + } + + /* + setTimeout + */ + + if ( bFuncSetTimeout == true ) + { + addError( "Do not utilize `setTimeout`, utilize Obsidian API -> `sleep`. E.g: `await sleep( X )`" ); + } + + /* + require("fs") + */ + + if ( bFuncFS_Chk1 == true || bFuncFS_Chk2 == true ) + { + addError( "`fs` import only available from Node.js runtime, this will throw errors for users running on mobile" ); + } + + /* + require("fs") / fs.existsSync + */ + + if ( bFuncFS_ExistsSync == true ) + { + addError( "`fs` import only available from Node.js runtime, this will throw errors for users running on mobile." ); + } + + /* + require("fs") / fs.mkdirSync + */ + + if ( bFuncFS_MkdirSync == true ) + { + addError( "`fs` import only available from Node.js runtime, this will throw errors for users running on mobile." ); + } + + /* + Generic Calls + */ + + if ( bContainsGeneric == true ) + { + addError( "Rename sample classes to something that makes sense. You are not allowed to have names such as: " + error_Generic ); + } + + /* + console.log found + */ + + /* + if ( bFuncConsoleLog == true ) + { + addWarning( "Avoid unnecessary logging or ensure logging only occurs in development environment." ); + } + */ + + /* + Bad words found + */ + + if ( bFoundBadWord == true && file != "package.json" && file != "manifest.json" ) + { + addWarning( "A restricted word was found in your code. Generic words are not allowed in strings such as: " + warn_BadWords ); + } + + if ( errors.length > 0 || warnings.length > 0 ) + { + + /* + Errors + */ + + if ( errors.length > 0 ) + { + message.push ( "\n\n\n> [!CAUTION]\n> Errors must be fixed prior to a pull request being reviewed and accepted.
The file `" + file + "` contains the following errors:\n\n
\n\n" ); + message = message.concat( errors ); + } + + /* + Warnings + */ + + if ( warnings.length > 0 ) + { + if ( errors.length > 0 ) + { + message.push ( "\n
\n
\n" ) + } + message.push ( "\n\n\n> [!WARNING]\n> Warnings are suggestions that do not require fixing, but are recommended before this pull request is reviewed and accepted.
The file `" + file + "` contains the following warnings:\n\n
\n\n" ); + message = message.concat( warnings ); + } + } + else + { + message.push ( "\n\n\n> [!NOTE]\n> The file `" + file + "` contains no errors\n\n
\n\n" ); + } + } + + if ( files_skipped.length > 0 ) + { + message.push ( "\n
\n\n---\n
\n" ); + message.push ( "### ❌ Skipped Files\n" ); + + message.push ( "\n\n\n> [!TIP]\n> The following file(s) have been skipped:\n\n
\n\n" ); + + for ( const file_skipped of files_skipped ) + { + message.push ( "- " + file_skipped ); + } + } + + /* + footer + */ + + message.push ( "\n
\n\n---\n
\n" ); + message.push ( `This check was done automatically. Do NOT open a new PR for re-validation. Instead, to trigger this check again, make a change to your PR and wait a few minutes, or close and re-open it.` ); + + /* + Has Errors + */ + + if ( bHasError == true ) + { + labels.push( "${{ env.LABEL_CHECK_STATUS_FAILED }}" ); + core.setFailed( "Pull Request Failed Autocheck: " + context.issue.number + ": " + context.payload.pull_request.title + "." ); + } + + /* + No Errors + */ + + if ( bHasError == false ) + { + + /* + change pr title + */ + + const pr_title = `${ context.payload.pull_request.title }`; + const pr_title_append = `PR ${ context.issue.number }:`; + + if ( !pr_title.startsWith( pr_title_append ) ) + { + await github.rest.pulls.update( + { + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: context.issue.number, + title: `${ pr_title_append } ${ context.payload.pull_request.title }` + } ); + } + + if ( !context.payload.pull_request.labels.filter( label => label.name === "${{ env.LABEL_CHECK_CHANGES_REQ }}" ).length > 0 ) + labels.push( "${{ env.LABEL_CHECK_REVIEW_READY }}" ); + } + + /* + Determine Labels + */ + + const bGitaction = type_gitaction.some( s => s.includes( branch_ref ) || branch_ref.includes( s ) ); + const bDependency = type_dependency.some( s => s.includes( branch_ref ) || branch_ref.includes( s ) ); + + if ( actor == "${{ env.BOT_NAME_DEPENDABOT }}" && bDependency ) + labels.push( "${{ env.LABEL_TYPE_DEPENDENCY }}" ); + else if ( actor == "${{ env.BOT_NAME_DEPENDABOT }}" && bGitaction ) + labels.push( "${{ env.LABEL_TYPE_GITACTION }}" ); + + if ( context.payload.pull_request.labels.filter( label => label.name === "${{ env.LABEL_CHECK_CHANGES_REQ }}" ).length > 0 ) + labels.push( "${{ env.LABEL_CHECK_CHANGES_REQ }}" ); + + if (context.payload.pull_request.labels.filter(label => label.name === "${{ env.LABEL_CHECK_REBASE_REQ }}" ).length > 0 ) + labels.push( "${{ env.LABEL_CHECK_REBASE_REQ }}" ); + + if ( context.payload.pull_request.labels.filter(label => label.name === "${{ env.LABEL_CHECK_SECURITY_ERR }}" ).length > 0 ) + labels.push( "${{ env.LABEL_CHECK_SECURITY_ERR }}" ); + + if (context.payload.pull_request.labels.filter( label => label.name === "${{ env.LABEL_CHECK_STATUS_CHGMADE }}" ).length > 0 ) + labels.push( "${{ env.LABEL_CHECK_STATUS_CHGMADE }}" ); + + if ( context.payload.pull_request.labels.filter( label => label.name === "${{ env.LABEL_CHECK_SCAN_SKIPPED }}" ).length > 0 ) + labels.push( "${{ env.LABEL_CHECK_SCAN_SKIPPED }}" ); + + labels.push( "${{ env.LABEL_TYPE_PR }}" ); + + /* + Set Label + */ + + await github.rest.issues.setLabels( + { + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + labels, + } ); + + /* + Create Comment + */ + + await github.rest.issues.createComment( + { + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: message.join('\n'), + } ); diff --git a/.github/workflows/issues-stale.yml b/.github/workflows/issues-stale.yml new file mode 100644 index 0000000..140c14d --- /dev/null +++ b/.github/workflows/issues-stale.yml @@ -0,0 +1,667 @@ +# # +# @type github workflow +# @desc creates repository labels if they are not yet installed +# issues marked as stale after 30 days, given tag Status 𐄂 Stale +# inactive issues closed after 180 days, given tag Status 𐄂 Locked +# inactive pr closed after 365 days, given tag Status 𐄂 Locked +# issues marked stale after 30 days, given tag Status 𐄂 Stale +# issues marked closed 7 days after being marked stale, given tag Status 𐄂 Autoclosed +# @author Aetherinox +# @url https://github.com/Aetherinox +# +# This Github action must be activated manually. This workflow script will do the +# following: +# +# - Scan issues / pull requests and make sure they have properly assigned labels: +# - `Bug` +# - `Feature` +# - `Urgent` +# - `Roadmap` +# +# - Workflow script will then scan each pr or issue and mark them as `Stale` +# if they haven't had any replies in 30 days. +# +# - Workflow will `autoclose` pr or issues which haven't had action in `365 days`. +# # + +name: "🎫 Issues › Stale" +run-name: "🎫 Issues › Stale" + +# # +# triggers +# # + +on: + workflow_dispatch: + schedule: + - cron: "0 0 * * *" + +# # +# environment variables +# # + +env: + PREFIX_BUG: "Bug" + PREFIX_DEPENDENCY: "Dependency" + PREFIX_DOCS: "Docs" + PREFIX_FEATURE: "Feature" + PREFIX_GIT: "Git Action" + PREFIX_PR: "PR" + PREFIX_ROADMAP: "Roadmap" + PREFIX_INTERNAL: "Internal" + PREFIX_URGENT: "Urgent" + + LABEL_BUG: "Type ◦ Bug" + LABEL_DEPENDENCY: "Type ◦ Dependency" + LABEL_DOCS: "Type ◦ Docs" + LABEL_FEATURE: "Type ◦ Feature" + LABEL_GIT: "Type ◦ Git Action" + LABEL_PR: "Type ◦ Pull Request" + LABEL_ROADMAP: "Type ◦ Roadmap" + LABEL_INTERNAL: "Type ◦ Internal" + LABEL_URGENT: "⚠ Urgent" + + BOT_NAME_1: EuropaServ + BOT_NAME_DEPENDABOT: dependabot[bot] + LABELS_JSON: | + [ + { "name": "AC › Changes Made", "color": "8F1784", "description": "Requested changes have been made and are pending a re-scan" }, + { "name": "AC › Changes Required", "color": "8F1784", "description": "Requires changes to be made to the package before being accepted" }, + { "name": "AC › Failed", "color": "a61f2d", "description": "Autocheck failed to run through a complete cycle, requires investigation" }, + { "name": "AC › Needs Rebase", "color": "8F1784", "description": "Due to the permissions on the requesting repo, this pull request must be rebased by the author" }, + { "name": "AC › Passed", "color": "146b4a", "description": "Ready to be reviewed" }, + { "name": "AC › Review Required", "color": "8F1784", "description": "PR needs to be reviewed by another person, after the requested changes have been made" }, + { "name": "AC › Security Warning", "color": "761620", "description": "Does not conform to developer policies, or includes potentially dangerous code" }, + { "name": "AC › Skipped Scan", "color": "8F1784", "description": "Author has skipped code scan" }, + { "name": "Status 𐄂 Duplicate", "color": "75536b", "description": "Issue or pull request already exists" }, + { "name": "Status 𐄂 Accepted", "color": "2e7539", "description": "This pull request has been accepted" }, + { "name": "Status 𐄂 Autoclosed", "color": "3E0915", "description": "Originally stale and was autoclosed for no activity" }, + { "name": "Status 𐄂 Denied", "color": "ba4058", "description": "Pull request has been denied" }, + { "name": "Status 𐄂 Locked", "color": "550F45", "description": "Automatically locked by AdminServ for a prolonged period of inactivity" }, + { "name": "Status 𐄂 Need Info", "color": "2E3C4C", "description": "Not enough information to resolve" }, + { "name": "Status 𐄂 No Action", "color": "030406", "description": "Closed without any action being taken" }, + { "name": "Status 𐄂 Pending", "color": "984b12", "description": "Pending pull request" }, + { "name": "Status 𐄂 Released", "color": "1b6626", "description": "Issues or PR has been implemented and is now live" }, + { "name": "Status 𐄂 Reopened", "color": "8a6f14", "description": "A previously closed PR which has been re-opened" }, + { "name": "Status 𐄂 Review", "color": "9e1451", "description": "Currently pending review" }, + { "name": "Status 𐄂 Stale", "color": "928282", "description": "Has not had any activity in over 30 days" }, + { "name": "Type ◦ Bug", "color": "9a2c2c", "description": "Something isn't working" }, + { "name": "Type ◦ Dependency", "color": "243759", "description": "Item is associated to dependency" }, + { "name": "Type ◦ Docs", "color": "0e588d", "description": "Improvements or modifications to docs" }, + { "name": "Type ◦ Feature", "color": "3c4e93", "description": "Feature request" }, + { "name": "Type ◦ Git Action", "color": "030406", "description": "GitHub Action / workflow" }, + { "name": "Type ◦ Pull Request", "color": "8F1784", "description": "Normal pull request" }, + { "name": "Type ◦ Roadmap", "color": "8F1784", "description": "Feature or bug currently planned for implementation" }, + { "name": "Type ◦ Internal", "color": "A51994", "description": "Assigned items are for internal developer use" }, + { "name": "Build ◦ Desktop", "color": "c7ca4a", "description": "Specific to desktop" }, + { "name": "Build ◦ Linux", "color": "c7ca4a", "description": "Specific to Linux" }, + { "name": "Build ◦ MacOS", "color": "c7ca4a", "description": "Specific to MacOS" }, + { "name": "Build ◦ Mobile", "color": "c7ca4a", "description": "Specific to mobile" }, + { "name": "Build ◦ Web", "color": "c7ca4a", "description": "Specific to web" }, + { "name": "Build ◦ Windows", "color": "c7ca4a", "description": "Specific to Windows" }, + { "name": "› API", "color": "F99B50", "description": "Plugin API, CLI, browser JS API" }, + { "name": "› Auto-type", "color": "9141E0", "description": "Auto-type functionality in desktop apps" }, + { "name": "› Browser", "color": "9141E0", "description": "Browser plugins and passing data to <=> from app" }, + { "name": "› Customization", "color": "E3F0FC", "description": "Customizations: plugins, themes, configs" }, + { "name": "› Design", "color": "FA70DE", "description": "Design related queries" }, + { "name": "› Dist", "color": "FA70DE", "description": "Installers and other forms of software distribution" }, + { "name": "› Enterprise", "color": "11447a", "description": "Issues about collaboration, administration, and so on" }, + { "name": "› Hardware", "color": "5a7503", "description": "YubiKey, other tokens, biometrics" }, + { "name": "› Import/Export", "color": "F5FFCC", "description": "Import from and export to different file formats" }, + { "name": "› Improvement", "color": "185c98", "description": "Enhance an existing feature" }, + { "name": "› Performance", "color": "006b75", "description": "Web and desktop performance issues" }, + { "name": "› Plugin Request", "color": "FCE9CA", "description": "Requested changes should be implemented as a plugin" }, + { "name": "› Security", "color": "F75D39", "description": "Security issues" }, + { "name": "› Self-Hosting", "color": "fad8c7", "description": "Self-hosting installations and configs" }, + { "name": "› Storage", "color": "5319e7", "description": "Storage providers: Dropbox, Google, WebDAV, etc." }, + { "name": "› Updater", "color": "1BADDE", "description": "Auto-updater issues" }, + { "name": "› UX", "color": "1BADDE", "description": "UX and usability" }, + { "name": "› Website", "color": "fef2c0", "description": "Website related issues" }, + { "name": "⚠ Urgent", "color": "a8740e", "description": "Requires urgent attention" }, + { "name": "⚠ Announcement", "color": "DB4712", "description": "Announcements" }, + { "name": "📰 Progress Report", "color": "392297", "description": "Development updates" }, + { "name": "📦 Release", "color": "277542", "description": "Release announcements" }, + { "name": "✔️ Poll", "color": "972255", "description": "Community polls" }, + { "name": "❔ Question", "color": "FFFFFF", "description": "All questions" } + ] + +# # +# jobs +# # + +jobs: + + # # + # Job [ Verify / Create Labels ] + # + # This job will ensure you have labels already created in your repo. + # All labels come from the JSON table LABELS_JSON. + # # + + job-labels-create: + name: >- + 🎫 Labels › Verify Existing + runs-on: ubuntu-latest + steps: + + # # + # [ Create Labels ] Start + # # + + - name: >- + ✅ Start + id: task_label_create_start + run: | + echo "Assigning labels and assignees" + + # # + # [ Create Labels ] Checkout + # # + + - name: >- + ☑️ Checkout + id: task_label_create_checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + # # + # [ Create Labels ] Verify Existing Labels + # # + + - name: >- + 🏷️ Verify Existing Labels + id: task_label_create_verify + uses: actions/github-script@v7 + with: + github-token: ${{ secrets.ADMINSERV_TOKEN_CL }} + script: | + const labels = JSON.parse( process.env.LABELS_JSON ); + for ( const label of labels ) + { + try + { + await github.rest.issues.createLabel( + { + owner: context.repo.owner, + repo: context.repo.repo, + name: label.name, + description: label.description || '', + color: label.color + }); + } + catch ( err ) + { + if ( err.status === 422 ) + { + console.log( `Label '${label.name}' already exists. Skipping.` ); + } + else + { + console.error( `Error creating label '${label.name}': ${err}` ); + } + } + } + + # # + # Job [ Check Labels ] + # + # Runs through all submissions to check for ones that have not been properly labeled + # - Bug + # - Feature + # - Urgent + # - Roadmap + # # + + job-issues-nolabel: + name: >- + 🎫 Labels › Assign Missing + runs-on: ubuntu-latest + needs: job-labels-create + steps: + + # # + # [ Check Labels ] Checkout + # # + + - name: "☑️ Prepare" + id: task_issues_nolabel_prepare + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + # # + # [ Check Labels ] Check + # Check if repo has labels currently added to issues + # # + + - name: 🏷️ Checking Issues + id: task_issues_nolabel_run + uses: actions/github-script@v7 + with: + github-token: ${{ secrets.ADMINSERV_TOKEN_CL }} + script: | + + /* + Date/Time + */ + + const dateTimeformat = ( date ) => + { + let month = date.getMonth( ) + 1; + month = month.toString( ).padStart( 2, '0' ); + let day = date.getDate( ).toString( ).padStart( 2, '0' ); + let year = date.getFullYear( ).toString( ).padStart( 2, '0' ); + + let hours = date.getHours(); + let minutes = date.getMinutes(); + let x = hours >= 12 ? 'PM' : 'AM'; + hours = hours % 12; + hours = hours ? hours : 12; + minutes = minutes.toString( ).padStart( 2, '0' ); + + let mergeTime = month + '.' + day + '.' + year + ' ' + hours + ':' + minutes + ' ' + x; + + return mergeTime; + } + + /* + Change last number ( 36 = hours ) + */ + + const expireAfterMs = 1000 * 60 * 60 * 36; // milliseconds ( 36 hours ) + const curtime = new Date( ).getTime( ); // 1711471510629 + const issues = await github.rest.issues.listForRepo( { owner: context.repo.owner, repo: context.repo.repo, state: 'open' } ); + + console.log( ` 📦── Found ${issues.data.length} open issues` ); + + for ( const issue of issues.data ) + { + + const author = `${ issue.user.login }`; + let date_UpdateDate = new Date( `${ issue.updated_at }` ?? `${ issue.created_at }` ); // Tue Mar 26 2024 16:40:41 GMT+0000 (Coordinated Universal Time) + date_UpdateDate.toISOString( ) // Tue Mar 26 2024 16:40:41 GMT+0000 (Coordinated Universal Time) (string) + + let date_UpdateHuman = dateTimeformat( date_UpdateDate ) + " UTC"; // 03.26.2024 4:40 PM UTC + const time_UpdateMs = new Date( issue.updated_at ).getTime( ); // 1711471241000 + + //if ( curtime < time_UpdateMs + expireAfterMs ) continue; + + /* + Anything past this point is stale / to be closed + */ + + const timeline = await github.rest.issues.listEventsForTimeline( { owner: context.repo.owner, repo: context.repo.repo, issue_number: issue.number } ); + // const labelEvent = timeline.data.find( event => event.event === 'labeled' && event.label.name === 'status-stale' ); + + /* + Get Issue Data + */ + + const add_labels = issue.labels.map( label => label.name ); + + let iss_title = `${ issue.title }`; + const iss_title_lc = iss_title.toLowerCase( ); + + let iss_body = `${ issue.body }`; + const iss_body_lc = iss_body.toLowerCase( ); + + console.log( ` └── 📁 ` + iss_title ); + console.log( ` └── 📄 Issue #${ issue.number } last updated on ${ date_UpdateHuman }` ); + console.log( ` └── 📄 ${add_labels}` ); + console.log( `\n\n` ) + + /* + Keywords + */ + + const bug_words = [ "bug", "broke", "issue", "fail" ]; + const feat_words = [ "feature", "request", "add support" ]; + const urgn_words = [ "urgent", "urgency", "emergency", "important", "critical" ]; + const road_words = [ "roadmap", "road map", "planned" ]; + + /* + Tags + */ + + const bug_tag = `${{ env.PREFIX_BUG }}:`; + const bug_lbl = `${{ env.LABEL_BUG }}`; + const feat_tag = `${{ env.PREFIX_FEATURE }}:`; + const feat_lbl = `${{ env.LABEL_FEATURE }}`; + const urgn_tag = `${{ env.PREFIX_URGENT }}:`; + const urgn_lbl = `${{ env.LABEL_URGENT }}`; + const road_tag = `${{ env.PREFIX_ROADMAP }}:`; + const road_lbl = `${{ env.LABEL_ROADMAP }}`; + + /* + Label > Bugs + */ + + const bug_bIncWordT = bug_words.some( s => s.includes( iss_title_lc ) || iss_title_lc.includes( s ) ); + + /* + Find regex based phrases + + Regex: + https://regex101.com/r/Z99Gnq/2 + */ + + const bug_findWordList = /^\b(?:I?\s*have\s*(?:a|an)\s*(?:issue|problem|bug))|(?:will\s*not\s*work)|(?:it\s*is\s*(?:broken|broke|stuck))|(?:found\s*(?:an?|the)\s*(?:bug|issue))|(?:can\s*I\s*fix\s*the\s*(?:bug|issue))|(?:(?:does not|doesn'?t|don'?t|won'?t|can'?t|can\s?not|will\s*not)\s*(?:work|load|function))|(?:it\s*(?:will\s?not|won'?t|can\s?not|can'?t))\s*(?:get|find)\s*the\s*(?:website|site|webpage|page)|(?:the\s*(?:window|frame)\s*is\s*(?:blank|white|empty|missing))\b$/igm; + const bug_bFoundMatchTitle = Boolean( bug_findWordList.test( iss_title ) ); + const bug_bFoundMatchBody = Boolean( bug_findWordList.test( iss_body ) ); + + /* + Do not change a title if the item starts with a PR: # + + Regex: + https://regex101.com/r/JOrqbN/1 + */ + + const bug_findPRTitle = /^PR\s?#?(?:[0-9]*:)/igm; + const bug_bFoundPRTitle = Boolean( bug_findPRTitle.test( iss_title ) ); + + /* + - Check if issue title matches the issue label "Bug:" + - Check if title contains word in containsList + */ + + if ( iss_title_lc.startsWith( bug_tag.toLowerCase( ) ) || bug_bIncWordT || bug_bFoundMatchTitle || bug_bFoundMatchBody ) + { + + add_labels.push( `${ bug_lbl }` ); + + if ( author === `${{ env.BOT_NAME_DEPENDABOT }}` ) + core.info( `Skipping: Detected ${ author }` ) + + // Rename title to contain Bug: + if ( author !== `${{ env.BOT_NAME_DEPENDABOT }}` && !bug_bFoundPRTitle && !iss_title_lc.startsWith( bug_tag.toLowerCase( ) ) && !iss_title_lc.startsWith( feat_tag.toLowerCase( ) ) && !iss_title_lc.startsWith( urgn_tag.toLowerCase( ) ) && !iss_title_lc.startsWith( road_tag.toLowerCase( ) ) ) + { + const title = issue.title; + let title_new = title.replace( /^\s?bug\s*(.*?)\b/gi, '' ); + title_new = title.replace( /^\s?fail\s*(.*?)\b/gi, '' ); + title_new = title.replace( /^\s?issue\s*(.*?)\b/gi, '' ); + iss_title = `${ bug_tag } ${ title_new }`; + } + + await github.rest.issues.update( + { + owner: context.repo.owner, repo: context.repo.repo, issue_number: issue.number, + title: `${ iss_title }`, labels: add_labels + } ); + } + + /* + Label > Features + */ + + const feat_bIncWordT = feat_words.some( s => s.includes( iss_title_lc ) || iss_title_lc.includes( s ) ); + + /* + Find regex based phrases + + Regex: + https://regex101.com/r/fR1Hm6/1 + */ + + const feat_findWordList = /^(?:(?:request|include|see)\s*(?:an?|the?)\s*(?:feature|addon|addition|plugin))|(?:(?:add|see|get)\s*support\s*(?:for|with|of))|(?:can\s*we\s*get\s*(?:the|a)\s*(?:ability|feature))|(?:💡 Feature:)$/igm; + const feat_bFoundMatchTitle = Boolean( feat_findWordList.test( iss_title ) ); + const feat_bFoundMatchBody = Boolean( feat_findWordList.test( iss_body ) ); + + /* + Do not change a title if the item starts with a PR: # + + Regex: + https://regex101.com/r/JOrqbN/1 + */ + + const feat_findPRTitle = /^PR\s?#?(?:[0-9]*:)/igm; + const feat_bFoundPRTitle = Boolean( feat_findPRTitle.test( iss_title ) ); + + /* + - Check if issue title matches the issue label "Feature:" + - Check if title contains word in containsList + */ + + if ( iss_title_lc.startsWith( feat_tag.toLowerCase( ) ) || feat_bIncWordT || feat_bFoundMatchTitle || feat_bFoundMatchBody ) + { + + add_labels.push( `${ feat_lbl }` ); + + if ( author === `${{ env.BOT_NAME_DEPENDABOT }}` ) + core.info( `Skipping: Detected ${ author }` ) + + // Rename title to contain Feature: + if ( author !== `${{ env.BOT_NAME_DEPENDABOT }}` && !feat_bFoundPRTitle && !iss_title_lc.startsWith( bug_tag.toLowerCase( ) ) && !iss_title_lc.startsWith( feat_tag.toLowerCase( ) ) && !iss_title_lc.startsWith( urgn_tag.toLowerCase( ) ) && !iss_title_lc.startsWith( road_tag.toLowerCase( ) ) ) + { + const title = issue.title; + let title_new = title.replace( /^\s?feature\s*(.*?)\b/gi, '' ); + title_new = title.replace( /^\s?request\s*(.*?)\b/gi, '' ); + title_new = title.replace( /^\s?add(.*?)\s?feature\s*(.*?)\b/gi, '' ); + title_new = title.replace( /^\s?add(.*?)\s?support\s*(.*?)\b/gi, '' ); + iss_title = `${ feat_tag } ${ title_new }`; + } + + await github.rest.issues.update( + { + owner: context.repo.owner, repo: context.repo.repo, issue_number: issue.number, + title: `${ iss_title }`, labels: add_labels + } ); + } + + /* + Label > Urgent + */ + + const urgn_bIncWordT = urgn_words.some( s => s.includes( iss_title_lc ) || iss_title_lc.includes( s ) ); + + /* + Find regex based phrases + + Regex: + https://regex101.com/r/eE9tJX/2 + */ + + const urgn_findWordList = /(?:(?:this)?is\s*a?n?\s*?(?:emergency|urgent|important|vital|acute|crucial|grave|pressing|serious|top.?priority|high.?priority))|(?:reply|respond|answer|write|address)\s*(?:immediate|quick|asap|urgent|now|fast|(?:as)?\s*(?:soon|quick|immediate|fast))(?:ly)?|(?:need\s*(?:help|support|fixed|answer|reply|response)!)|(?:emergency|critical|urgen(?:t|cy)|high.?priority)/igm; + const urgn_bFoundMatchTitle = Boolean( urgn_findWordList.test( iss_title ) ); + const urgn_bFoundMatchBody = Boolean( urgn_findWordList.test( iss_body ) ); + + /* + Do not change a title if the item starts with a PR: # + + Regex: + https://regex101.com/r/JOrqbN/1 + */ + + const urgn_findPRTitle = /^PR\s?#?(?:[0-9]*:)/igm; + const urgn_bFoundPRTitle = Boolean( urgn_findPRTitle.test( iss_title ) ); + + /* + - Check if issue title matches the issue label "Urgent:" + - Check if title contains word in containsList + */ + + if ( iss_title_lc.startsWith( urgn_tag.toLowerCase( ) ) || urgn_bIncWordT || urgn_bFoundMatchTitle || urgn_bFoundMatchBody ) + { + + add_labels.push( `${ urgn_lbl }` ); + + if ( author === `${{ env.BOT_NAME_DEPENDABOT }}` ) + core.info( `Skipping: Detected ${ author }` ) + + // Rename title to contain Urgent: + if ( author !== `${{ env.BOT_NAME_DEPENDABOT }}` && !urgn_bFoundPRTitle && !iss_title_lc.startsWith( bug_tag.toLowerCase( ) ) && !iss_title_lc.startsWith( feat_tag.toLowerCase( ) ) && !iss_title_lc.startsWith( urgn_tag.toLowerCase( ) ) && !iss_title_lc.startsWith( road_tag.toLowerCase( ) ) ) + { + const title = issue.title; + let title_new = title.replace( /^\s?emergency\s*(.*?)\b/gi, '' ); + title_new = title.replace( /^\s?urgent\s*(.*?)\b/gi, '' ); + title_new = title.replace( /^\s?urgency\s*(.*?)\b/gi, '' ); + title_new = title.replace( /^\s?important\s*(.*?)\b/gi, '' ); + title_new = title.replace( /^\s?critical\s*(.*?)\b/gi, '' ); + iss_title = `${ urgn_tag } ${ title_new }`; + } + + await github.rest.issues.update( + { + owner: context.repo.owner, repo: context.repo.repo, issue_number: issue.number, + title: `${ iss_title }`, labels: add_labels + } ); + } + + /* + Label > Roadmap + */ + + const road_bIncWordT = road_words.some( s => s.includes( iss_title_lc ) || iss_title_lc.includes( s ) ); + + /* + Find regex based phrases + Roadmap requires headers #Summary and #Proposal | #Objective + + Regex: + https://regex101.com/r/ucajBZ/1 + */ + + const road_findWordList = /#\s*Summary[\S\s]+#\s*(?:Proposal|Objective)[^\]]+/igm; + const road_bFoundMatchTitle = Boolean( road_findWordList.test( iss_title ) ); + const road_bFoundMatchBody = Boolean( road_findWordList.test( iss_body ) ); + + /* + Do not change a title if the item starts with a PR: # + + Regex: + https://regex101.com/r/JOrqbN/1 + */ + + const road_findPRTitle = /^PR\s?#?(?:[0-9]*:)/igm; + const road_bFoundPRTitle = Boolean( road_findPRTitle.test( iss_title ) ); + + /* + - Check if issue title matches the issue label "Roadmap:" + - Check if title contains word in containsList + */ + + if ( iss_title_lc.startsWith( road_tag.toLowerCase( ) ) || road_bIncWordT || road_bFoundMatchTitle || road_bFoundMatchBody ) + { + + add_labels.push( `${ road_lbl }` ); + + if ( author === `${{ env.BOT_NAME_DEPENDABOT }}` ) + core.info( `Skipping: Detected ${ author }` ) + + // Rename title to contain Roadmap: + if ( author !== `${{ env.BOT_NAME_DEPENDABOT }}` && !road_bFoundPRTitle && !iss_title_lc.startsWith( bug_tag.toLowerCase( ) ) && !iss_title_lc.startsWith( feat_tag.toLowerCase( ) ) && !iss_title_lc.startsWith( urgn_tag.toLowerCase( ) ) && !iss_title_lc.startsWith( road_tag.toLowerCase( ) ) ) + { + const title = issue.title; + let title_new = title.replace( /^\s?emergency\s*(.*?)\b/gi, '' ); + title_new = title.replace( /^\s?urgent\s*(.*?)\b/gi, '' ); + title_new = title.replace( /^\s?urgency\s*(.*?)\b/gi, '' ); + title_new = title.replace( /^\s?important\s*(.*?)\b/gi, '' ); + title_new = title.replace( /^\s?critical\s*(.*?)\b/gi, '' ); + iss_title = `${ road_tag } ${ title_new }`; + } + + await github.rest.issues.update( + { + owner: context.repo.owner, repo: context.repo.repo, issue_number: issue.number, + title: `${ iss_title }`, labels: add_labels + } ); + } + + /* + await github.rest.issues.update( + { + owner: context.repo.owner, repo: context.repo.repo, issue_number: issue.number, + state: 'closed', state_reason: 'not planned' + } ); + */ + } + + # # + # Job [ Stale Issues ] + # # + + job-issues-stale: + name: >- + 💤 Check › Stale + runs-on: ubuntu-latest + needs: + - job-labels-create + - job-issues-nolabel + permissions: + contents: write + issues: write + pull-requests: write + steps: + + # # + # [ Stale Issues ] Check Condition + # # + + - name: "💤 Stale › Check Condition" + uses: actions/stale@v9 + id: task_issues_stale_run + with: + repo-token: ${{ secrets.ADMINSERV_TOKEN_CL }} + stale-issue-message: | + ⚠️ It looks like there hasn't been any recent updates on this issue. If you created this issue and no longer consider it + open, then please login to github and close the issue. + + If there is no further activity on this issue, it will be automatically closed in the next week. + + --- + + I am a bot reaching out to you with an automated response. + stale-issue-label: 'Status 𐄂 Stale' + close-issue-label: 'Status 𐄂 Autoclosed' + exempt-issue-labels: 'Status 𐄂 Accepted,Status 𐄂 Review,Status 𐄂 Pending,Type ◦ Bug,Type ◦ Dependency,Type ◦ Docs,Type ◦ Feature,Type ◦ Git Action,Type ◦ Pull Request,Type ◦ Roadmap' + days-before-stale: 14 + days-before-close: 7 + days-before-pr-stale: -1 + days-before-pr-close: -1 + + # # + # Job [ Lock Issues ] + # # + + job-issues-lock: + name: >- + 🔒 Check › Inactive + runs-on: ubuntu-latest + needs: + - job-labels-create + - job-issues-nolabel + steps: + + # # + # [ Lock Issues ] Look for inactives + # # + + - name: "🔒 Lock › Inactives" + uses: dessant/lock-threads@v5 + id: task_issues_lock_run + with: + github-token: ${{ secrets.ADMINSERV_TOKEN_CL }} + exclude-any-issue-labels: 'AC › Review Required,Status 𐄂 Accepted,Status 𐄂 Review,Status 𐄂 Pending,Type ◦ Bug,Type ◦ Dependency,Type ◦ Docs,Type ◦ Feature,Type ◦ Git Action,Type ◦ Roadmap,Type ◦ Internal' + add-issue-labels: 'Status 𐄂 Locked' + issue-inactive-days: '60' + issue-lock-reason: 'issue inactive for 60 days' + issue-comment: > + ⚠️ This **issue** has been automatically locked since there has not been any recent activity after it was closed. + + Please open a new issue for related bugs. + + --- + + I am a bot reaching out to you with an automated response. + exclude-any-pr-labels: 'AC › Review Required,Status 𐄂 Accepted,Status 𐄂 Review,Status 𐄂 Pending,Type ◦ Bug,Type ◦ Dependency,Type ◦ Docs,Type ◦ Feature,Type ◦ Git Action,Type ◦ Roadmap,Type ◦ Internal' + add-pr-labels: 'Status 𐄂 Locked' + pr-inactive-days: '365' + pr-lock-reason: 'pr inactive for 365 days' + pr-comment: > + ⚠️ This **pull request** has been automatically locked since there has not been any recent activity after it was closed. + + Please open a new issue for related bugs. + + --- + + I am a bot reaching out to you with an automated response. diff --git a/.github/workflows/labels-create.yml b/.github/workflows/labels-create.yml new file mode 100644 index 0000000..e5e4759 --- /dev/null +++ b/.github/workflows/labels-create.yml @@ -0,0 +1,182 @@ +# # +# @type github workflow +# @desc manually activated workflow to create issue labels +# @author Aetherinox +# @url https://github.com/Aetherinox +# +# This Github action must be activated manually. This workflow script will do the +# following: +# +# - Scan issues / pull requests and make sure they have properly assigned labels: +# - `Bug` +# - `Feature` +# - `Urgent` +# - `Roadmap` +# +# - Workflow script will then scan each pr or issue and mark them as `Stale` +# if they haven't had any replies in 30 days. +# +# - Workflow will `autoclose` pr or issues which haven't had action in `365 days`. +# # + +name: "🎫 Labels › Create" +run-name: "🎫 Labels › Create" + +# # +# triggers +# # + +on: + workflow_dispatch: + +# # +# environment variables +# # + +env: + BOT_NAME_1: AdminServ + BOT_NAME_2: AdminServX + BOT_NAME_3: EuropaServ + BOT_NAME_DEPENDABOT: dependabot[bot] + LABELS_JSON: | + [ + { "name": "AC › Changes Made", "color": "8F1784", "description": "Requested changes have been made and are pending a re-scan" }, + { "name": "AC › Changes Required", "color": "8F1784", "description": "Requires changes to be made to the package before being accepted" }, + { "name": "AC › Failed", "color": "a61f2d", "description": "Autocheck failed to run through a complete cycle, requires investigation" }, + { "name": "AC › Needs Rebase", "color": "8F1784", "description": "Due to the permissions on the requesting repo, this pull request must be rebased by the author" }, + { "name": "AC › Passed", "color": "146b4a", "description": "Ready to be reviewed" }, + { "name": "AC › Review Required", "color": "8F1784", "description": "PR needs to be reviewed by another person, after the requested changes have been made" }, + { "name": "AC › Security Warning", "color": "761620", "description": "Does not conform to developer policies, or includes potentially dangerous code" }, + { "name": "AC › Skipped Scan", "color": "8F1784", "description": "Author has skipped code scan" }, + { "name": "Status 𐄂 Duplicate", "color": "75536b", "description": "Issue or pull request already exists" }, + { "name": "Status 𐄂 Accepted", "color": "2e7539", "description": "This pull request has been accepted" }, + { "name": "Status 𐄂 Autoclosed", "color": "3E0915", "description": "Originally stale and was autoclosed for no activity" }, + { "name": "Status 𐄂 Denied", "color": "ba4058", "description": "Pull request has been denied" }, + { "name": "Status 𐄂 Locked", "color": "550F45", "description": "Automatically locked by AdminServ for a prolonged period of inactivity" }, + { "name": "Status 𐄂 Need Info", "color": "2E3C4C", "description": "Not enough information to resolve" }, + { "name": "Status 𐄂 No Action", "color": "030406", "description": "Closed without any action being taken" }, + { "name": "Status 𐄂 Pending", "color": "984b12", "description": "Pending pull request" }, + { "name": "Status 𐄂 Released", "color": "1b6626", "description": "Issues or PR has been implemented and is now live" }, + { "name": "Status 𐄂 Reopened", "color": "8a6f14", "description": "A previously closed PR which has been re-opened" }, + { "name": "Status 𐄂 Review", "color": "9e1451", "description": "Currently pending review" }, + { "name": "Status 𐄂 Stale", "color": "928282", "description": "Has not had any activity in over 30 days" }, + { "name": "Type ◦ Bug", "color": "9a2c2c", "description": "Something isn't working" }, + { "name": "Type ◦ Dependency", "color": "243759", "description": "Item is associated to dependency" }, + { "name": "Type ◦ Docs", "color": "0e588d", "description": "Improvements or modifications to docs" }, + { "name": "Type ◦ Feature", "color": "3c4e93", "description": "Feature request" }, + { "name": "Type ◦ Git Action", "color": "030406", "description": "GitHub Action / workflow" }, + { "name": "Type ◦ Pull Request", "color": "8F1784", "description": "Normal pull request" }, + { "name": "Type ◦ Roadmap", "color": "8F1784", "description": "Feature or bug currently planned for implementation" }, + { "name": "Type ◦ Internal", "color": "A51994", "description": "Assigned items are for internal developer use" }, + { "name": "Build ◦ Desktop", "color": "c7ca4a", "description": "Specific to desktop" }, + { "name": "Build ◦ Linux", "color": "c7ca4a", "description": "Specific to Linux" }, + { "name": "Build ◦ MacOS", "color": "c7ca4a", "description": "Specific to MacOS" }, + { "name": "Build ◦ Mobile", "color": "c7ca4a", "description": "Specific to mobile" }, + { "name": "Build ◦ Web", "color": "c7ca4a", "description": "Specific to web" }, + { "name": "Build ◦ Windows", "color": "c7ca4a", "description": "Specific to Windows" }, + { "name": "› API", "color": "F99B50", "description": "Plugin API, CLI, browser JS API" }, + { "name": "› Auto-type", "color": "9141E0", "description": "Auto-type functionality in desktop apps" }, + { "name": "› Browser", "color": "9141E0", "description": "Browser plugins and passing data to <=> from app" }, + { "name": "› Customization", "color": "E3F0FC", "description": "Customizations: plugins, themes, configs" }, + { "name": "› Design", "color": "FA70DE", "description": "Design related queries" }, + { "name": "› Dist", "color": "FA70DE", "description": "Installers and other forms of software distribution" }, + { "name": "› Enterprise", "color": "11447a", "description": "Issues about collaboration, administration, and so on" }, + { "name": "› Hardware", "color": "5a7503", "description": "YubiKey, other tokens, biometrics" }, + { "name": "› Import/Export", "color": "F5FFCC", "description": "Import from and export to different file formats" }, + { "name": "› Improvement", "color": "185c98", "description": "Enhance an existing feature" }, + { "name": "› Performance", "color": "006b75", "description": "Web and desktop performance issues" }, + { "name": "› Plugin Request", "color": "FCE9CA", "description": "Requested changes should be implemented as a plugin" }, + { "name": "› Security", "color": "F75D39", "description": "Security issues" }, + { "name": "› Self-Hosting", "color": "fad8c7", "description": "Self-hosting installations and configs" }, + { "name": "› Storage", "color": "5319e7", "description": "Storage providers: Dropbox, Google, WebDAV, etc." }, + { "name": "› Updater", "color": "1BADDE", "description": "Auto-updater issues" }, + { "name": "› UX", "color": "1BADDE", "description": "UX and usability" }, + { "name": "› Website", "color": "fef2c0", "description": "Website related issues" }, + { "name": "⚠ Urgent", "color": "a8740e", "description": "Requires urgent attention" }, + { "name": "⚠ Announcement", "color": "DB4712", "description": "Announcements" }, + { "name": "📰 Progress Report", "color": "392297", "description": "Development updates" }, + { "name": "📦 Release", "color": "277542", "description": "Release announcements" }, + { "name": "✔️ Poll", "color": "972255", "description": "Community polls" }, + { "name": "❔ Question", "color": "FFFFFF", "description": "All questions" } + ] + +# # +# jobs +# # + +jobs: + + # # + # Job [ Verify / Create Labels ] + # + # This job will ensure you have labels already created in your repo. + # All labels come from the JSON table LABELS_JSON. + # # + + issues-labels-create: + name: >- + 🎫 Labels › Create + runs-on: ubuntu-latest + permissions: + contents: 'read' + id-token: 'write' + issues: 'write' + steps: + + # # + # [ Create Labels ] Start + # # + + - name: >- + ✅ Start + id: task_label_create_start + run: | + echo "Assigning labels and assignees" + + # # + # [ Create Labels ] Checkout + # # + + - name: >- + ☑️ Checkout + id: task_label_create_checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + # # + # [ Create Labels ] Verify Existing Labels + # # + + - name: >- + 🏷️ Verify Existing Labels + uses: actions/github-script@v7 + with: + github-token: ${{ secrets.ADMINSERV_TOKEN_CL }} + script: | + const labels = JSON.parse( process.env.LABELS_JSON ); + for ( const label of labels ) + { + try + { + await github.rest.issues.createLabel( + { + owner: context.repo.owner, + repo: context.repo.repo, + name: label.name, + description: label.description || '', + color: label.color + }); + } + catch ( err ) + { + if ( err.status === 422 ) + { + console.log( `Label '${label.name}' already exists. Skipping.` ); + } + else + { + console.error( `Error creating label '${label.name}': ${err}` ); + } + } + } diff --git a/.github/workflows/ping-developer.yml b/.github/workflows/ping-developer.yml new file mode 100644 index 0000000..34434b2 --- /dev/null +++ b/.github/workflows/ping-developer.yml @@ -0,0 +1,175 @@ +# # +# @type github workflow +# @desc pings the developer +# @author Aetherinox +# @url https://github.com/Aetherinox +# # + +name: "⚙️ Ping › Developer" +run-name: "⚙️ Ping › Developer" + +# # +# triggers +# # + +on: + issue_comment: + types: [created] + +# # +# environment variables +# # + +env: + BOT_NAME_1: AdminServ + BOT_NAME_2: AdminServX + BOT_NAME_3: EuropaServ + BOT_NAME_DEPENDABOT: dependabot[bot] + +# # +# jobs +# +# env not available for job.if +# # + +jobs: + deploy: + runs-on: ubuntu-latest + if: | + contains(github.event.comment.body, '/ping') + steps: + + # # + # Job > Complete > Get publish timestamp + # # + + - name: "🕛 Get Timestamp" + id: task_complete_timestamp_get + run: | + echo "NOW=$(date +'%m-%d-%Y %H:%M:%S')" >> $GITHUB_ENV + + # # + # Add Label to accepted PR + # + # port 465 + # server_port: 465 + # secure: true + # ignore_cert: false + # + # port 587 + # server_port: 587 + # secure: false + # # + + - name: Send mail + uses: dawidd6/action-send-mail@v3 + with: + server_address: ${{secrets.EMAIL_SMTP}} + server_port: 465 + secure: true + username: ${{secrets.EMAIL_FROM}} + password: ${{secrets.EMAIL_KEY}} + subject: "Github: Ping notification from ${{ github.repository }}" + to: ${{secrets.EMAIL_TO}} + from: ${{secrets.EMAIL_FROM}} + html_body: | + + + + + Title + + + + + +
+
+
+ +
+
+ +
+

[Github] Dear ${{github.repository_owner}},

+


You have received a ping notification from ${{ github.repository }} by ${{ github.event.comment.user.login }}.

+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + +
Repository${{ github.repository }}
Date${{ env.NOW }}
Commenter${{ github.event.comment.user.login }}
Issue #${{ github.event.issue.number }}
ActionNotification
+
+ +

+ +
+
+ + + +
+
+ +

 

+


~ Github +

+
+ +

+ +
+ Copyright © 2024 - Betelgeuse +
+
+ + + ignore_cert: true + convert_markdown: true + priority: normal