diff --git a/.github/DISCUSSION_TEMPLATE/accessibility.yml b/.github/DISCUSSION_TEMPLATE/accessibility.yml new file mode 100644 index 0000000..4531777 --- /dev/null +++ b/.github/DISCUSSION_TEMPLATE/accessibility.yml @@ -0,0 +1,71 @@ +labels: [Accessibility] +body: +- type: markdown + attributes: + value: + + ⚠️ This category is solely for discussions and feedback on [the digital accessibility of GitHub products](https://accessibility.github.com/). ⚠️ + + Please use this space for questions, bug reports, opportunities, and feedback related to making GitHub features accessible to users of all abilities. + + To ensure you receive a timely response and connect with the proper team(s), please post in the correct category, questions about access, 2FA, and SMS belong in the [General category](https://github.com/orgs/community/discussions/categories/general?discussions_q=is%3Aopen+category%3AGeneral). Discussions that are posted in the wrong category may be moved. Users who post spam or violate of our [Code of Conduct](https://github.com/github/.github/blob/95e9dcb0b8b70a25cdb75de29f600812a07eb996/CODE_OF_CONDUCT.md) will be blocked. +- type: dropdown + attributes: + label: Select Topic Area + description: What would you like to discuss? + options: + - "Question" + - "Product Feedback" + - "Bug" + - "Show & Tell" + - "General" + validations: + required: true +- type: dropdown + attributes: + label: GitHub Feature Area + description: > + What GitHub product/feature area is your Accessibility post related to? + options: + - Actions + - APIs and Webhooks + - Code Scanning + - Code Search and Navigation + - Code Security + - Codespaces + - Command Palette + - Copilot + - Dependabot + - Discussions + - Enterprise + - Feed + - GHAS + - Gists + - GitHub Desktop + - GitHub Education + - Issues + - Lists + - Markdown + - Mobile + - Notifications + - Packages + - Pages + - Profile + - Projects + - Pull Requests + - Repositories + - Security and Privacy + - Sponsors + - UI/UX + - Visual Studio + - VSCode + - Wiki + - Other + validations: + required: true +- type: textarea + attributes: + label: Body + description: Start your discussion! + validations: + required: true diff --git a/.github/DISCUSSION_TEMPLATE/actions.yml b/.github/DISCUSSION_TEMPLATE/actions.yml new file mode 100644 index 0000000..f1c3bf3 --- /dev/null +++ b/.github/DISCUSSION_TEMPLATE/actions.yml @@ -0,0 +1,20 @@ +labels: [Actions] +body: +- type: dropdown + attributes: + label: Select Topic Area + description: What would you like to discuss? + options: + - "Question" + - "Product Feedback" + - "Bug" + - "Show & Tell" + - "General" + validations: + required: true +- type: textarea + attributes: + label: Body + description: Start your discussion! + validations: + required: true diff --git a/.github/DISCUSSION_TEMPLATE/api-and-webhooks.yml b/.github/DISCUSSION_TEMPLATE/api-and-webhooks.yml new file mode 100644 index 0000000..6a5dcdd --- /dev/null +++ b/.github/DISCUSSION_TEMPLATE/api-and-webhooks.yml @@ -0,0 +1,20 @@ +labels: [API and Webhooks] +body: +- type: dropdown + attributes: + label: Select Topic Area + description: What would you like to discuss? + options: + - "Question" + - "Product Feedback" + - "Bug" + - "Show & Tell" + - "General" + validations: + required: true +- type: textarea + attributes: + label: Body + description: Start your discussion! + validations: + required: true diff --git a/.github/DISCUSSION_TEMPLATE/code-search-and-navigation.yml b/.github/DISCUSSION_TEMPLATE/code-search-and-navigation.yml new file mode 100644 index 0000000..3369489 --- /dev/null +++ b/.github/DISCUSSION_TEMPLATE/code-search-and-navigation.yml @@ -0,0 +1,20 @@ +labels: [Code Search and Navigation] +body: +- type: dropdown + attributes: + label: Select Topic Area + description: What would you like to discuss? + options: + - "Question" + - "Product Feedback" + - "Bug" + - "Show & Tell" + - "General" + validations: + required: true +- type: textarea + attributes: + label: Body + description: Start your discussion! + validations: + required: true diff --git a/.github/DISCUSSION_TEMPLATE/code-security.yml b/.github/DISCUSSION_TEMPLATE/code-security.yml new file mode 100644 index 0000000..c166f96 --- /dev/null +++ b/.github/DISCUSSION_TEMPLATE/code-security.yml @@ -0,0 +1,20 @@ +labels: [Code Security] +body: +- type: dropdown + attributes: + label: Select Topic Area + description: What would you like to discuss? + options: + - "Question" + - "Product Feedback" + - "Bug" + - "Show & Tell" + - "General" + validations: + required: true +- type: textarea + attributes: + label: Body + description: Start your discussion! + validations: + required: true \ No newline at end of file diff --git a/.github/DISCUSSION_TEMPLATE/codespaces.yml b/.github/DISCUSSION_TEMPLATE/codespaces.yml new file mode 100644 index 0000000..4722569 --- /dev/null +++ b/.github/DISCUSSION_TEMPLATE/codespaces.yml @@ -0,0 +1,22 @@ +labels: [Codespaces] +body: +- type: dropdown + id: topic + attributes: + label: Select Topic Area + description: What would you like to discuss? + options: + - "Question" + - "Product Feedback" + - "Bug" + - "Show & Tell" + - "General" + validations: + required: true +- type: textarea + id: text + attributes: + label: Body + description: Start your discussion! + validations: + required: true diff --git a/.github/DISCUSSION_TEMPLATE/copilot.yml b/.github/DISCUSSION_TEMPLATE/copilot.yml new file mode 100644 index 0000000..ac3e732 --- /dev/null +++ b/.github/DISCUSSION_TEMPLATE/copilot.yml @@ -0,0 +1,20 @@ +labels: [Copilot] +body: +- type: dropdown + attributes: + label: Select Topic Area + description: What would you like to discuss? + options: + - "Question" + - "Product Feedback" + - "Bug" + - "Show & Tell" + - "General" + validations: + required: true +- type: textarea + attributes: + label: Body + description: Start your discussion! + validations: + required: true diff --git a/.github/DISCUSSION_TEMPLATE/discussions.yml b/.github/DISCUSSION_TEMPLATE/discussions.yml new file mode 100644 index 0000000..c654758 --- /dev/null +++ b/.github/DISCUSSION_TEMPLATE/discussions.yml @@ -0,0 +1,28 @@ +labels: [Discussions] +body: +- type: markdown + attributes: + value: | + ⚠️ This category is solely for discussions and feedback on [the GitHub Discussions product](https://github.com/features/discussions). ⚠️ + + The Discussions category is NOT meant to be a place for any old discussion regarding GitHub or programming — [the General category](https://github.com/orgs/community/discussions/categories/general) is a more appropriate space for misc topics. To ensure you receive a timely response and connect with the proper team(s), please post in the correct category by using the corresponding product category. + + NOTE: If you post in this category about topics other than GitHub Discussions, your post may be moved or deleted without warning. Users who post spam or violate of our [Code of Conduct](https://github.com/github/.github/blob/95e9dcb0b8b70a25cdb75de29f600812a07eb996/CODE_OF_CONDUCT.md) will be blocked. +- type: dropdown + attributes: + label: Select Topic Area + description: What would you like to discuss? + options: + - "Question" + - "Product Feedback" + - "Bug" + - "Show & Tell" + - "General" + validations: + required: true +- type: textarea + attributes: + label: Body + description: Start your discussion! + validations: + required: true diff --git a/.github/DISCUSSION_TEMPLATE/enterprise.yml b/.github/DISCUSSION_TEMPLATE/enterprise.yml new file mode 100644 index 0000000..f18530b --- /dev/null +++ b/.github/DISCUSSION_TEMPLATE/enterprise.yml @@ -0,0 +1,20 @@ +labels: [Enterprise] +body: +- type: dropdown + attributes: + label: Select Topic Area + description: What would you like to discuss? + options: + - "Question" + - "Product Feedback" + - "Bug" + - "Show & Tell" + - "General" + validations: + required: true +- type: textarea + attributes: + label: Body + description: Start your discussion! + validations: + required: true diff --git a/.github/DISCUSSION_TEMPLATE/feed.yml b/.github/DISCUSSION_TEMPLATE/feed.yml new file mode 100644 index 0000000..904944a --- /dev/null +++ b/.github/DISCUSSION_TEMPLATE/feed.yml @@ -0,0 +1,20 @@ +labels: [Feed] +body: +- type: dropdown + attributes: + label: Select Topic Area + description: What would you like to discuss? + options: + - "Question" + - "Product Feedback" + - "Bug" + - "Show & Tell" + - "General" + validations: + required: true +- type: textarea + attributes: + label: Body + description: Start your discussion! + validations: + required: true diff --git a/.github/DISCUSSION_TEMPLATE/general.yml b/.github/DISCUSSION_TEMPLATE/general.yml new file mode 100644 index 0000000..299e572 --- /dev/null +++ b/.github/DISCUSSION_TEMPLATE/general.yml @@ -0,0 +1,24 @@ +labels: [General] +body: +- type: markdown + attributes: + value: + "⚠️ Please note: The Community can not help with account-related issues, such as resinstatement requests or help to escalate your [GitHub Support](https://support.github.com/contact) tickets. ⚠️" +- type: dropdown + attributes: + label: Select Topic Area + description: What would you like to discuss? + options: + - "Question" + - "Product Feedback" + - "Bug" + - "Show & Tell" + - "General" + validations: + required: true +- type: textarea + attributes: + label: Body + description: Start your discussion! + validations: + required: true diff --git a/.github/DISCUSSION_TEMPLATE/github-education.yml b/.github/DISCUSSION_TEMPLATE/github-education.yml new file mode 100644 index 0000000..c2d50bc --- /dev/null +++ b/.github/DISCUSSION_TEMPLATE/github-education.yml @@ -0,0 +1,35 @@ +body: + - type: markdown + attributes: + value: | + ## We’re really excited you’re here! 👋 🎉 Welcome to the GitHub Education community! 🎒 + - type: markdown + attributes: + value: | + Before jumping into your conversation please read the hints and tips below to help you create a great discussion topic others would more likely respond to 💬 + - type: markdown + attributes: + value: | + #### Are you asking for help with your GitHub Global Campus application process? + * **Help us to help you** 👍 Provide as much relevant information about your application as possible including posting your **latest rejection reasons**. + * Do **not post** any personal information on the discussions boards e.g images of your evidence. + + #### Need Octernships project guidance? + * We **can not** provide specific project guidance and can only recommend you [reach out to your Octernships project maintainers](https://github.com/orgs/community/discussions/51456#discussioncomment-5542896) for instructions or to raise a bug. + - type: textarea + id: conversation + attributes: + label: Hi everyone, + description: "Start your conversation below👇. Ensure you have read and adhere to our [Code of Conduct](https://github.com/community/community/blob/main/CODE_OF_CONDUCT.md). For more expectations around feedback responses - check out the [Community ReadMe](https://github.com/community/community#from-a-suggestion-to-a-shipped-feature)." + validations: + required: true + - type: markdown + attributes: + value: | + #### Anything else I should check before posting? 🤔 + * Check out the available documentation answering commonly asked questions: [Octernships FAQs](https://github.com/orgs/community/discussions/49380); [Student Developer Pack Application & FAQs](https://github.com/orgs/community/discussions/17814); [Campus Experts](https://education.github.com/students/experts). + * Search these boards for previous threads describing similar problems. ⭐ ***Hint:*** *search by rejection reason* + - type: markdown + attributes: + value: | + Remember to mark an [answer as "solved"](https://docs.github.com/en/discussions/collaborating-with-your-community-using-discussions/participating-in-a-discussion#marking-a-comment-as-an-answer) to help others with similar questions. ✅ diff --git a/.github/DISCUSSION_TEMPLATE/lists.yml b/.github/DISCUSSION_TEMPLATE/lists.yml new file mode 100644 index 0000000..05f80d6 --- /dev/null +++ b/.github/DISCUSSION_TEMPLATE/lists.yml @@ -0,0 +1,20 @@ +labels: [Lists] +body: +- type: dropdown + attributes: + label: Select Topic Area + description: What would you like to discuss? + options: + - "Question" + - "Product Feedback" + - "Bug" + - "Show & Tell" + - "General" + validations: + required: true +- type: textarea + attributes: + label: Body + description: Start your discussion! + validations: + required: true diff --git a/.github/DISCUSSION_TEMPLATE/mobile.yml b/.github/DISCUSSION_TEMPLATE/mobile.yml new file mode 100644 index 0000000..1594bcf --- /dev/null +++ b/.github/DISCUSSION_TEMPLATE/mobile.yml @@ -0,0 +1,20 @@ +labels: [Mobile] +body: +- type: dropdown + attributes: + label: Select Topic Area + description: What would you like to discuss? + options: + - "Question" + - "Product Feedback" + - "Bug" + - "Show & Tell" + - "General" + validations: + required: true +- type: textarea + attributes: + label: Body + description: Start your discussion! + validations: + required: true diff --git a/.github/DISCUSSION_TEMPLATE/models.yml b/.github/DISCUSSION_TEMPLATE/models.yml new file mode 100644 index 0000000..4e3af2f --- /dev/null +++ b/.github/DISCUSSION_TEMPLATE/models.yml @@ -0,0 +1,20 @@ +labels: [Models] +body: +- type: dropdown + attributes: + label: Select Topic Area + description: What would you like to discuss? + options: + - "Question" + - "Product Feedback" + - "Bug" + - "Show & Tell" + - "General" + validations: + required: true +- type: textarea + attributes: + label: Body + description: Start your discussion! + validations: + required: true diff --git a/.github/DISCUSSION_TEMPLATE/packages.yml b/.github/DISCUSSION_TEMPLATE/packages.yml new file mode 100644 index 0000000..926dc8e --- /dev/null +++ b/.github/DISCUSSION_TEMPLATE/packages.yml @@ -0,0 +1,20 @@ +labels: [Packages] +body: +- type: dropdown + attributes: + label: Select Topic Area + description: What would you like to discuss? + options: + - "Question" + - "Product Feedback" + - "Bug" + - "Show & Tell" + - "General" + validations: + required: true +- type: textarea + attributes: + label: Body + description: Start your discussion! + validations: + required: true \ No newline at end of file diff --git a/.github/DISCUSSION_TEMPLATE/pages.yml b/.github/DISCUSSION_TEMPLATE/pages.yml new file mode 100644 index 0000000..143bb43 --- /dev/null +++ b/.github/DISCUSSION_TEMPLATE/pages.yml @@ -0,0 +1,20 @@ +labels: [Pages] +body: +- type: dropdown + attributes: + label: Select Topic Area + description: What would you like to discuss? + options: + - "Question" + - "Product Feedback" + - "Bug" + - "Show & Tell" + - "General" + validations: + required: true +- type: textarea + attributes: + label: Body + description: Start your discussion! + validations: + required: true diff --git a/.github/DISCUSSION_TEMPLATE/profile.yml b/.github/DISCUSSION_TEMPLATE/profile.yml new file mode 100644 index 0000000..d2cdac0 --- /dev/null +++ b/.github/DISCUSSION_TEMPLATE/profile.yml @@ -0,0 +1,20 @@ +labels: [Profile] +body: +- type: dropdown + attributes: + label: Select Topic Area + description: What would you like to discuss? + options: + - "Question" + - "Product Feedback" + - "Bug" + - "Show & Tell" + - "General" + validations: + required: true +- type: textarea + attributes: + label: Body + description: Start your discussion! + validations: + required: true \ No newline at end of file diff --git a/.github/DISCUSSION_TEMPLATE/programming-help.yml b/.github/DISCUSSION_TEMPLATE/programming-help.yml new file mode 100644 index 0000000..498d95b --- /dev/null +++ b/.github/DISCUSSION_TEMPLATE/programming-help.yml @@ -0,0 +1,19 @@ +labels: [Programming Help] +body: +- type: markdown + attributes: + value: Start your discussion! + validations: + required: true +- type: textarea + attributes: + label: Body + description: Start your discussion! + validations: + required: true +- type: checkboxes + attributes: + label: Guidelines + options: + - label: I have read and understood this category's [guidelines](https://github.com/orgs/community/discussions/51384) before making this post. + required: true diff --git a/.github/DISCUSSION_TEMPLATE/projects-and-issues.yml b/.github/DISCUSSION_TEMPLATE/projects-and-issues.yml new file mode 100644 index 0000000..50c5080 --- /dev/null +++ b/.github/DISCUSSION_TEMPLATE/projects-and-issues.yml @@ -0,0 +1,65 @@ +body: + - type: markdown + attributes: + value: > + ⚠️ This category is solely for conversations related to the GitHub + feature areas + [Issues](https://docs.github.com/en/issues/tracking-your-work-with-issues/about-issues) + and + [Projects](https://docs.github.com/en/issues/planning-and-tracking-with-projects/learning-about-projects/about-projects) + ⚠️ + - type: dropdown + attributes: + label: Select Topic Area + description: What would you like to discuss? + options: + - Question + - Product Feedback + - Bug + - Show & Tell + - General + validations: + required: true + - type: dropdown + attributes: + label: Feature Area + description: > + What GitHub product/feature area would you like to discuss? This + category is only for conversations related to the below. + + Issues (The feature that allows you to track ideas, feedback, tasks, or bugs for work on GitHub. Not for any issue you are having). + + Projects (For planning and tracking work on GitHub - not any open source or other type of project). + options: + - Issues + - Projects + validations: + required: true + - type: textarea + attributes: + label: Body + description: Start your discussion! + placeholder: >- + STOP! This category is only for discussions on GitHub Issues and + GitHub Projects. GitHub Issues are the issues you open in a repo to + track your work. NOT issues you are having with GitHub or a GitHub + product. GitHub Projects are project boards you can create for example + Kanban boards, to track your work. NOT projects on GitHub. + + + We know the "issues" and ‘projects” naming conventions can be a little confusing. + + + To report an issue with another area of the GitHub, use the correct product category, if one does not exist, use the General category. To ensure you receive a timely response and connect with the proper team(s) make sure you are posting in the correct category. + validations: + required: true + - type: checkboxes + attributes: + label: Guidelines + options: + - label: I have read the above statement and can confirm my post is relevant to + the GitHub feature areas + [Issues](https://docs.github.com/en/issues/tracking-your-work-with-issues/about-issues) + and/or + [Projects](https://docs.github.com/en/issues/planning-and-tracking-with-projects/learning-about-projects/about-projects). + required: true diff --git a/.github/DISCUSSION_TEMPLATE/pull-requests.yml b/.github/DISCUSSION_TEMPLATE/pull-requests.yml new file mode 100644 index 0000000..f1c7906 --- /dev/null +++ b/.github/DISCUSSION_TEMPLATE/pull-requests.yml @@ -0,0 +1,20 @@ +labels: [Pull Requests] +body: +- type: dropdown + attributes: + label: Select Topic Area + description: What would you like to discuss? + options: + - "Question" + - "Product Feedback" + - "Bug" + - "Show & Tell" + - "General" + validations: + required: true +- type: textarea + attributes: + label: Body + description: Start your discussion! + validations: + required: true diff --git a/.github/DISCUSSION_TEMPLATE/repositories.yml b/.github/DISCUSSION_TEMPLATE/repositories.yml new file mode 100644 index 0000000..6af09a4 --- /dev/null +++ b/.github/DISCUSSION_TEMPLATE/repositories.yml @@ -0,0 +1,20 @@ +labels: [Repositories] +body: +- type: dropdown + attributes: + label: Select Topic Area + description: What would you like to discuss? + options: + - "Question" + - "Product Feedback" + - "Bug" + - "Show & Tell" + - "General" + validations: + required: true +- type: textarea + attributes: + label: Body + description: Start your discussion! + validations: + required: true diff --git a/.github/DISCUSSION_TEMPLATE/sponsors.yml b/.github/DISCUSSION_TEMPLATE/sponsors.yml new file mode 100644 index 0000000..c80941d --- /dev/null +++ b/.github/DISCUSSION_TEMPLATE/sponsors.yml @@ -0,0 +1,20 @@ +labels: [Sponsors] +body: +- type: dropdown + attributes: + label: Select Topic Area + description: What would you like to discuss? + options: + - "Question" + - "Product Feedback" + - "Bug" + - "Show & Tell" + - "General" + validations: + required: true +- type: textarea + attributes: + label: Body + description: Start your discussion! + validations: + required: true diff --git a/.github/actions/add_feedback_comment b/.github/actions/add_feedback_comment new file mode 100644 index 0000000..183420f --- /dev/null +++ b/.github/actions/add_feedback_comment @@ -0,0 +1,43 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +require_relative "../lib/github" + +owner = "community" +repo = "community" + +body =< discussion_data.json + + echo 'DISCUSSION_BODY_HTML='$(jq '.data.repository.discussion.bodyHTML' discussion_data.json) >> $GITHUB_ENV + echo 'DISCUSSION_ID='$(jq '.data.repository.discussion.id' discussion_data.json) >> $GITHUB_ENV + + - run: npm install jsdom + + - name: Get selected topic + id: get_selected_topic + uses: actions/github-script@v7 + with: + result-encoding: string + script: | + try { + const jsdom = require('jsdom'); + const { JSDOM } = jsdom; + const { DISCUSSION_BODY_HTML } = process.env + + const fragment = JSDOM.fragment(DISCUSSION_BODY_HTML); + const featureAreaHeaders = fragment.querySelectorAll("h3"); + const featureAreaHeader = Array.from(featureAreaHeaders).find(header => header.textContent.trim().toLowerCase() === 'github feature area'); + if (!featureAreaHeader) { + return ""; + } + + const selectedTopicElement = featureAreaHeader.nextElementSibling; + if (!selectedTopicElement) { + return ""; + } + + const selectedTopic = selectedTopicElement.textContent.trim().toLowerCase(); + const validTopics = ["actions", "apis and webhooks", "code scanning", "code search and navigation", "code security", "codespaces", "command palette", "copilot", "dependabot", "discussions", "enterprise", "feed", "ghas", "gists", "github desktop", "github education", "issues", "lists", "markdown", "mobile", "notifications", "packages", "pages", "profile", "projects", "pull requests", "repositories", "security and privacy", "sponsors", "ui/ux", "visual studio", "vscode", "wiki"]; + return validTopics.includes(selectedTopic) ? selectedTopic : ""; + + } catch (error) { + return ""; + } + + - name: Fetch label id for selected topic + id: fetch_label_id + if: ${{ steps.get_selected_topic.outputs.result != '' }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + OWNER: ${{ github.repository_owner }} + REPO: ${{ github.event.repository.name }} + TOPIC: ${{ steps.get_selected_topic.outputs.result }} + run: | + gh api graphql -F owner=$OWNER -F name=$REPO -F topic="$TOPIC" -f query=' + query($owner: String!, $name: String!, $topic: String) { + repository(owner: $owner, name: $name){ + labels(first: 1, query: $topic) { + edges { + node { + id + name + } + } + } + } + }' > repository_label_data.json + + echo 'LABEL_ID='$(jq '.data.repository.labels.edges[].node | .id ' repository_label_data.json) >> $GITHUB_ENV + + - name: Label the discussion + if: ${{ steps.get_selected_topic.outputs.result != '' }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + gh api graphql -f query=' + mutation($labelableId: ID!, $labelIds: [ID!]!) { + addLabelsToLabelable(input: {labelableId: $labelableId, labelIds: $labelIds}) { + labelable { + labels(first: 10) { + edges { + node { + id + name + } + } + } + } + } + }' -f labelableId=$DISCUSSION_ID -f labelIds=$LABEL_ID diff --git a/.github/workflows/add-feedback-comment.yml b/.github/workflows/add-feedback-comment.yml new file mode 100644 index 0000000..e9b641f --- /dev/null +++ b/.github/workflows/add-feedback-comment.yml @@ -0,0 +1,28 @@ +name: Add a feedback comment when a discussion gets a related label + +on: + discussion: + types: [created,labeled] + +jobs: + build: + runs-on: ubuntu-latest + permissions: + contents: read + discussions: write + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + + - name: Bundle install + run: bundle install + + - name: Add feedback comment + run: .github/actions/add_feedback_comment + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + DISCUSSION_NUMBER: ${{ github.event.discussion.number }} + NODE_ID: ${{ github.event.discussion.node_id }} diff --git a/.github/workflows/auto-close-pr.yml b/.github/workflows/auto-close-pr.yml new file mode 100644 index 0000000..29e2a70 --- /dev/null +++ b/.github/workflows/auto-close-pr.yml @@ -0,0 +1,55 @@ +name: Auto Close PRs + +on: + pull_request_target: + types: [opened, reopened] + +jobs: + check_pr: + name: Check PR + runs-on: ubuntu-latest + + steps: + - name: Check if employee + id: check_employee + uses: actions/github-script@v7 + with: + github-token: ${{ secrets.READ_GITHUB_ORG_MEMBERS_TOKEN }} + result-encoding: string + script: | + try { + const response = await github.rest.orgs.checkMembershipForUser({ + org: `github`, + username: context.payload.pull_request.user.login + }); + + if (response.status === 204) { + return true; + } else { + return false; + } + } catch (error) { + console.log(error); + return 'false'; + } + + - name: Close PR + id: close_pr + if: ${{ steps.check_employee.outputs.result == 'false' }} + uses: actions/github-script@v7 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const body = `This pull request is being automatically closed because we do not accept external contributions to this repository.`; + + await github.rest.issues.createComment({ + ...context.repo, + issue_number: context.issue.number, + body: body + }); + + await github.rest.pulls.update({ + ...context.repo, + pull_number: context.payload.pull_request.number, + state: 'closed' + }); diff --git a/.github/workflows/close-dormant-discussions.yml b/.github/workflows/close-dormant-discussions.yml new file mode 100644 index 0000000..135b716 --- /dev/null +++ b/.github/workflows/close-dormant-discussions.yml @@ -0,0 +1,29 @@ +name: Close out dormant discussions + +on: + workflow_dispatch: + schedule: + # At 01:23 every day + - cron: '23 1 * * *' + +jobs: + build: + runs-on: ubuntu-latest + permissions: + contents: read + discussions: write + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + + - name: Bundle install + run: bundle install + + - name: Close out discussions + run: .github/actions/close + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + diff --git a/.github/workflows/comment-on-dormant-discussions.yml b/.github/workflows/comment-on-dormant-discussions.yml new file mode 100644 index 0000000..929e08c --- /dev/null +++ b/.github/workflows/comment-on-dormant-discussions.yml @@ -0,0 +1,28 @@ +name: Comment on dormant discussions + +on: + workflow_dispatch: + schedule: + # At 03:23 every day + - cron: '23 3 * * *' + +jobs: + build: + runs-on: ubuntu-latest + permissions: + contents: read + discussions: write + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + + - name: Bundle install + run: bundle install + + - name: Comment on discussions + run: .github/actions/comment + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/issue_projects_labeler.yml b/.github/workflows/issue_projects_labeler.yml new file mode 100644 index 0000000..787453c --- /dev/null +++ b/.github/workflows/issue_projects_labeler.yml @@ -0,0 +1,110 @@ +name: Issue and Project Templated Discussions + +on: + discussion: + types: [created] + +jobs: + label-templated-discussion: + runs-on: ubuntu-latest + + steps: + - name: Get discussion body html + id: get_discussion_body_html + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + OWNER: ${{ github.repository_owner }} + REPO: ${{ github.event.repository.name }} + DISCUSSION_NUMBER: ${{ github.event.discussion.number }} + run: | + gh api graphql -F owner=$OWNER -F name=$REPO -F number=$DISCUSSION_NUMBER -f query=' + query($owner: String!, $name: String!, $number: Int!) { + repository(owner: $owner, name: $name){ + discussion(number: $number) { + bodyHTML + id + } + } + }' > discussion_data.json + + echo 'DISCUSSION_BODY_HTML='$(jq '.data.repository.discussion.bodyHTML' discussion_data.json) >> $GITHUB_ENV + echo 'DISCUSSION_ID='$(jq '.data.repository.discussion.id' discussion_data.json) >> $GITHUB_ENV + + - run: npm install jsdom + + - name: Get selected topic + id: get_selected_topic + uses: actions/github-script@v7 + with: + result-encoding: string + script: | + try { + const jsdom = require('jsdom'); + const { JSDOM } = jsdom; + const { DISCUSSION_BODY_HTML } = process.env + + const fragment = JSDOM.fragment(DISCUSSION_BODY_HTML); + const featureAreaHeaders = fragment.querySelectorAll("h3"); + const featureAreaHeader = Array.from(featureAreaHeaders).find(header => header.textContent.trim().toLowerCase() === 'feature area'); + if (!featureAreaHeader) { + return ""; + } + + const selectedTopicElement = featureAreaHeader.nextElementSibling; + if (!selectedTopicElement) { + return ""; + } + + const selectedTopic = selectedTopicElement.textContent.trim().toLowerCase(); + const validTopics = ["projects", "issues"]; + return validTopics.includes(selectedTopic) ? selectedTopic : ""; + + } catch (error) { + return ""; + } + + - name: Fetch label id for selected topic + id: fetch_label_id + if: ${{ steps.get_selected_topic.outputs.result != '' }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + OWNER: ${{ github.repository_owner }} + REPO: ${{ github.event.repository.name }} + TOPIC: ${{ steps.get_selected_topic.outputs.result }} + run: | + gh api graphql -F owner=$OWNER -F name=$REPO -F topic="$TOPIC" -f query=' + query($owner: String!, $name: String!, $topic: String) { + repository(owner: $owner, name: $name){ + labels(first: 1, query: $topic) { + edges { + node { + id + name + } + } + } + } + }' > repository_label_data.json + + echo 'LABEL_ID='$(jq '.data.repository.labels.edges[].node | .id ' repository_label_data.json) >> $GITHUB_ENV + + - name: Label the discussion + if: ${{ steps.get_selected_topic.outputs.result != '' }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + gh api graphql -f query=' + mutation($labelableId: ID!, $labelIds: [ID!]!) { + addLabelsToLabelable(input: {labelableId: $labelableId, labelIds: $labelIds}) { + labelable { + labels(first: 10) { + edges { + node { + id + name + } + } + } + } + } + }' -f labelableId=$DISCUSSION_ID -f labelIds=$LABEL_ID diff --git a/.github/workflows/label-templated-discussions.yml b/.github/workflows/label-templated-discussions.yml new file mode 100644 index 0000000..96dca34 --- /dev/null +++ b/.github/workflows/label-templated-discussions.yml @@ -0,0 +1,111 @@ +name: Label Templated Discussions + +on: + discussion: + types: [created] + +jobs: + label-templated-discussion: + runs-on: ubuntu-latest + + steps: + - name: Get discussion body html + id: get_discussion_body_html + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + OWNER: ${{ github.repository_owner }} + REPO: ${{ github.event.repository.name }} + DISCUSSION_NUMBER: ${{ github.event.discussion.number }} + run: | + gh api graphql -F owner=$OWNER -F name=$REPO -F number=$DISCUSSION_NUMBER -f query=' + query($owner: String!, $name: String!, $number: Int!) { + repository(owner: $owner, name: $name){ + discussion(number: $number) { + bodyHTML + id + } + } + }' > discussion_data.json + + echo 'DISCUSSION_BODY_HTML='$(jq '.data.repository.discussion.bodyHTML' discussion_data.json) >> $GITHUB_ENV + echo 'DISCUSSION_ID='$(jq '.data.repository.discussion.id' discussion_data.json) >> $GITHUB_ENV + + - run: npm install jsdom + + - name: Get selected topic + id: get_selected_topic + uses: actions/github-script@v7 + with: + result-encoding: string + script: | + try { + const jsdom = require('jsdom'); + const { JSDOM } = jsdom; + const { DISCUSSION_BODY_HTML } = process.env + + const fragment = JSDOM.fragment(DISCUSSION_BODY_HTML); + const selectTopicHeader = fragment.querySelector("h3"); + const templated = selectTopicHeader.textContent === 'Select Topic Area'; + if (!templated) { + return ""; + } + + const selectedTopicElement = selectTopicHeader.nextElementSibling; + if (!selectedTopicElement) { + return ""; + } + + const selectedTopic = selectedTopicElement.textContent.trim().toLowerCase(); + const validTopics = ["product feedback", "question", "bug", "show & tell", "general"]; + return validTopics.includes(selectedTopic) ? selectedTopic : ""; + + } catch (error) { + return ""; + } + + - name: Fetch label id for selected topic + id: fetch_label_id + if: ${{ steps.get_selected_topic.outputs.result != '' }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + OWNER: ${{ github.repository_owner }} + REPO: ${{ github.event.repository.name }} + TOPIC: ${{ steps.get_selected_topic.outputs.result }} + run: | + gh api graphql -F owner=$OWNER -F name=$REPO -F topic="$TOPIC" -f query=' + query($owner: String!, $name: String!, $topic: String) { + repository(owner: $owner, name: $name){ + labels(first: 1, query: $topic) { + edges { + node { + id + name + } + } + } + } + }' > repository_label_data.json + + echo 'LABEL_ID='$(jq '.data.repository.labels.edges[].node | .id ' repository_label_data.json) >> $GITHUB_ENV + + - name: Label the discussion + if: ${{ steps.get_selected_topic.outputs.result != '' }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + gh api graphql -f query=' + mutation($labelableId: ID!, $labelIds: [ID!]!) { + addLabelsToLabelable(input: {labelableId: $labelableId, labelIds: $labelIds}) { + labelable { + labels(first: 10) { + edges { + node { + id + name + } + } + } + } + } + }' -f labelableId=$DISCUSSION_ID -f labelIds=$LABEL_ID + diff --git a/.github/workflows/recategorize-discussions.yml b/.github/workflows/recategorize-discussions.yml new file mode 100644 index 0000000..6a982d4 --- /dev/null +++ b/.github/workflows/recategorize-discussions.yml @@ -0,0 +1,104 @@ +# This is a basic workflow that is manually triggered + +name: Recategorize labeled discussions + +# Controls when the action will run. Workflow runs when manually triggered using the UI +# or API. +on: + workflow_dispatch: + # Inputs the workflow accepts. + inputs: + label: + # Friendly description to be shown in the UI instead of 'name' + description: 'Label for discussions that will be recategorized' + # Default value if no value is explicitly provided + default: 'bug' + # Input has to be provided for the workflow to run + required: true + # The data type of the input + type: string + category: + # Friendly description to be shown in the UI instead of 'name' + description: 'The name of the category the labeled discussions will be moved to' + # Default value if no value is explicitly provided + default: 'Ideas' + # Input has to be provided for the workflow to run + required: true + # The data type of the input + type: string + max_to_move: + description: 'The maximum number of discussions we should try to move (up to 100), helpful in testing' + type: number + default: 50 + required: true + +# A workflow run is made up of one or more jobs that can run sequentially or in parallel +jobs: + # This workflow contains a single job called "greet" + recategorize-discussions: + # The type of runner that the job will run on + runs-on: ubuntu-latest + + # Steps represent a sequence of tasks that will be executed as part of the job + steps: + # Runs a single command using the runners shell + - name: Send greeting + run: echo "Moving Discussions labeled ${{ inputs.label }} to category ${{ inputs.category }}" + # Use the graphql search api to find all discussions with the label using the search() query + - name: Find labeled discussions + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + searchQuery="label:\"${{ inputs.label }}\" repo:${{ github.repository}} -category:\"${{ inputs.category }}\"" + discussions="$(gh api graphql -F queryString="$searchQuery" -f query=' + query($queryString: String!) { + search(query:$queryString,type:DISCUSSION,first:${{ inputs.max_to_move }}){ + nodes{ + ...on Discussion{id} + } + } + }' | jq -r '.data.search.nodes[].id')" + echo 'Found discussions: '"$discussions" + echo 'DISCUSSIONS_TO_TRANSFER='$discussions >> $GITHUB_ENV + - name: Find the id of the discussion category in the current repository + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + # split github.repository into owner and name + repoName=$(echo ${{ github.repository }} | cut -d '/' -f 2) + found_category=$(gh api graphql -F name="$repoName" -F owner="${{ github.repository_owner }}" -f query=' + query($name: String!, $owner: String!){ + repository(owner:$owner, name:$name){ + discussionCategories(first:100){ + nodes{ + id + name + } + } + } + }' | jq -r --arg category "${{ inputs.category }}" '.data.repository.discussionCategories.nodes[] | select(.name==$category) | .id') + echo 'CATEGORY_ID='$found_category >> $GITHUB_ENV + - name: Dryrun + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + echo "Dryrun" + echo "DISCUSSIONS_TO_TRANSFER=$DISCUSSIONS_TO_TRANSFER" + echo "CATEGORY_ID=$CATEGORY_ID" + - name: Move discussions + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + # DISCUSSIONS_TO_TRANSFER is in the form "id_1 id_2 id_3" + # Loop over the ids and use the updateDiscussion mutation to move the discussion to the new category + for discussion_id in $DISCUSSIONS_TO_TRANSFER; do + echo "Moving discussion $discussion_id to category $CATEGORY_ID" + gh api graphql -F discussionId="$discussion_id" -F categoryId="$CATEGORY_ID" -f query=' + mutation($discussionId: ID!, $categoryId: ID!) { + updateDiscussion(input: {discussionId: $discussionId, categoryId: $categoryId}) { + discussion { + number + } + } + }' + done diff --git a/.github/workflows/remove-governed-labels-on-create.yml b/.github/workflows/remove-governed-labels-on-create.yml new file mode 100644 index 0000000..766a5f6 --- /dev/null +++ b/.github/workflows/remove-governed-labels-on-create.yml @@ -0,0 +1,113 @@ +name: 'On Create: Remove Governed Label' + +# Trigger workflow whenever a discussion is created +on: + discussion: + types: [created] + +jobs: + check_labels: + name: Check labels + runs-on: ubuntu-latest + +# Step 1 is to check if the discussion creator is an employee. If employee = 'true', later steps will not fire. If employee = 'false', later steps will continue + steps: + - name: Check if employee + id: check_employee + uses: actions/github-script@v7 + with: + github-token: ${{ secrets.READ_GITHUB_ORG_MEMBERS_TOKEN }} + result-encoding: string + script: | + console.log(context.payload); + + try { + const response = await github.rest.orgs.checkMembershipForUser({ + org: `github`, + username: context.payload.sender.login + }); + + if (response.status === 204) { + return 'true'; + } else { + return 'false'; + } + } catch (error) { + console.log(error); + return 'false'; + } + +# Step 2 grabs the label id of the label we want to remove and stores it in the GITHUB_ENV. We use the 'TOPIC' to define the label in plain text + - name: Fetch label id + id: fetch_label_id + if: ${{ steps.check_employee.outputs.result == 'false' }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + OWNER: ${{ github.repository_owner }} + REPO: ${{ github.event.repository.name }} + TOPIC: 'more information needed' + run: | + gh api graphql -F owner=$OWNER -F name=$REPO -F topic="$TOPIC" -f query=' + query($owner: String!, $name: String!, $topic: String) { + repository(owner: $owner, name: $name){ + labels(first: 3, query: $topic) { + edges { + node { + id + name + } + } + } + } + }' > repository_label_data.json + + echo 'LABEL_ID='$(jq '.data.repository.labels.edges[].node | .id ' repository_label_data.json) >> $GITHUB_ENV + +# Step 3 is to record the discussion id of the discussion that triggered the workflow in the GITHUB_ENV so the workflow knows which discussion post to then act on later + - name: Fetch discussion ID + id: fetch_discussion_id + if: ${{ steps.check_employee.outputs.result == 'false' }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + OWNER: ${{ github.repository_owner }} + REPO: ${{ github.event.repository.name }} + TOPIC: 'more information needed' + run: | + gh api graphql -F owner=$OWNER -F name=$REPO -f query=' + query($owner: String!, $name: String!) { + repository(owner: $owner, name: $name) { + discussion(number: ${{ github.event.discussion.number }}) { + id + } + } + }' > discussion_data.json + + echo 'DISCUSSION_ID='$(jq '.data.repository.discussion.id' discussion_data.json) >> $GITHUB_ENV + +# Step 4 recalls the appropriate discussion id and label id from the GITHUB_ENV and removes the label from the discussion + - name: Unlabel the discussion + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + OWNER: ${{ github.repository_owner }} + REPO: ${{ github.event.repository.name }} + DISCUSSION_ID: ${{ env.DISCUSSION_ID }} + LABEL_IDS: ${{ env.LABEL_ID }} + run: | + IFS=' ' read -ra labelIds <<< "$LABEL_IDS" + for labelId in "${labelIds[@]}"; do + gh api graphql -f query=' + mutation($labelableId: ID!, $labelIds: [ID!]!) { + removeLabelsFromLabelable(input: {labelableId: $labelableId, labelIds: $labelIds}) { + labelable { + labels(first: 10) { + edges { + node { + id + name + } + } + } + } + } + }' -f labelableId=$DISCUSSION_ID -f labelIds=$labelId + done diff --git a/.github/workflows/remove-governed-labels-on-label.yml b/.github/workflows/remove-governed-labels-on-label.yml new file mode 100644 index 0000000..21cc965 --- /dev/null +++ b/.github/workflows/remove-governed-labels-on-label.yml @@ -0,0 +1,70 @@ +name: 'On Label: Remove Governed Label' + +# Trigger workflow whenever a discussion is labeled +on: + discussion: + types: [labeled] + +permissions: + contents: read + discussions: write + +jobs: + action: + runs-on: ubuntu-latest +# Step 1 is to check if the user applying the label is an employee. If employee = 'true', later steps will not fire. If employee = 'false', later steps will continue + steps: + - name: Check if employee + id: check_employee + uses: actions/github-script@v7 + with: + github-token: ${{ secrets.READ_GITHUB_ORG_MEMBERS_TOKEN }} + result-encoding: string + script: | + try { + const response = await github.rest.orgs.checkMembershipForUser({ + org: `github`, + username: context.payload.sender.login + }); + + if (response.status === 204) { + return 'true'; + } else { + return 'false'; + } + } catch (error) { + console.log(error); + return 'false'; + } + +# Step 2 is to record the discussion id of the discussion that triggered the workflow in the GITHUB_ENV so the workflow knows which discussion post to then act on later + - name: Fetch discussion ID + id: fetch_discussion_id + if: ${{ steps.check_employee.outputs.result == 'false' && github.event.repository && github.event.discussion }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + OWNER: ${{ github.repository_owner }} + REPO: ${{ github.event.repository.name }} + run: | + gh api graphql -F owner=$OWNER -F name=$REPO -f query=' + query($owner: String!, $name: String!) { + repository(owner: $owner, name: $name) { + discussion(number: ${{ github.event.discussion.number }}) { + id + } + } + }' > discussion_data.json + + echo 'DISCUSSION_ID='$(jq '.data.repository.discussion.id' discussion_data.json) >> $GITHUB_ENV + +# Step 3 references the label-actions.yml file for instructions. That config file tells the action what to do next. +# config file will reference the GITHUB_ENV and will only process the 'unlabel' action on 'discussions' and will unlabel the named labels + - name: Label Actions + if: ${{ steps.check_employee.outputs.result == 'false' }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + OWNER: ${{ github.repository_owner }} + REPO: ${{ github.event.repository.name }} + config-path: '.github/label-actions.yml' + process-only: 'discussions' + uses: dessant/label-actions@v3 diff --git a/.github/workflows/remove-inactive-label.yml b/.github/workflows/remove-inactive-label.yml new file mode 100644 index 0000000..eae2661 --- /dev/null +++ b/.github/workflows/remove-inactive-label.yml @@ -0,0 +1,27 @@ +name: Remove the inactive label + +on: + discussion_comment: + types: [created] + +jobs: + build: + runs-on: ubuntu-latest + permissions: + contents: read + discussions: write + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + + - name: Bundle install + run: bundle install + + - name: Remove the label + run: .github/actions/remove_inactive_label + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + NODE_ID: ${{ github.event.discussion.node_id }}