Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .github/FUNDING.yml
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This PR shouldn't affect unrelated files

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Restored - this was deleted accidentally.

Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
github: learningequality
custom: ["https://learningequality.org/donate/", "https://www.every.org/learningequality"]

Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
name: Manage issue header
name: Handle issue label events
on:
issues:
types: [opened, reopened, labeled, unlabeled]
types: [labeled, unlabeled]
jobs:
call-workflow:
name: Call shared workflow
uses: learningequality/.github/.github/workflows/manage-issue-header.yml@main
uses: learningequality/.github/.github/workflows/issue-label.yml@main
secrets:
LE_BOT_APP_ID: ${{ secrets.LE_BOT_APP_ID }}
LE_BOT_PRIVATE_KEY: ${{ secrets.LE_BOT_PRIVATE_KEY }}
11 changes: 11 additions & 0 deletions .github/workflows/call-issue-open.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
name: Handle issue open events
on:
issues:
types: [opened, reopened]
jobs:
call-workflow:
name: Call shared workflow
uses: learningequality/.github/.github/workflows/issue-open.yml@main
secrets:
LE_BOT_APP_ID: ${{ secrets.LE_BOT_APP_ID }}
LE_BOT_PRIVATE_KEY: ${{ secrets.LE_BOT_PRIVATE_KEY }}
40 changes: 40 additions & 0 deletions .github/workflows/good-first-issue-comment.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
name: Post good first issue guidance comment
on:
workflow_call:
secrets:
LE_BOT_APP_ID:
description: 'GitHub App ID for authentication'
required: true
LE_BOT_PRIVATE_KEY:
description: 'GitHub App Private Key for authentication'
required: true
jobs:
good-first-issue-comment:
runs-on: ubuntu-latest
steps:
- name: Generate GitHub token
id: generate-token
uses: actions/create-github-app-token@v3
with:
app-id: ${{ secrets.LE_BOT_APP_ID }}
private-key: ${{ secrets.LE_BOT_PRIVATE_KEY }}
- name: Checkout .github repository
uses: actions/checkout@v6
with:
repository: learningequality/.github
ref: main
token: ${{ steps.generate-token.outputs.token }}
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: 20
cache: 'yarn'
- name: Install dependencies
run: yarn install --frozen-lockfile
- name: Run script
uses: actions/github-script@v8
with:
github-token: ${{ steps.generate-token.outputs.token }}
script: |
const script = require('./scripts/good-first-issue-comment.js');
await script({github, context, core});
27 changes: 27 additions & 0 deletions .github/workflows/issue-label.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
name: Handle issue label events
on:
workflow_call:
secrets:
LE_BOT_APP_ID:
description: 'GitHub App ID for authentication'
required: true
LE_BOT_PRIVATE_KEY:
description: 'GitHub App Private Key for authentication'
required: true
jobs:
manage-issue-header:
name: Manage issue header
if: >-
(github.event.action == 'labeled' && github.event.label.name == 'help wanted') || (github.event.action == 'unlabeled' && github.event.label.name == 'help wanted')
uses: learningequality/.github/.github/workflows/manage-issue-header.yml@main
secrets:
LE_BOT_APP_ID: ${{ secrets.LE_BOT_APP_ID }}
LE_BOT_PRIVATE_KEY: ${{ secrets.LE_BOT_PRIVATE_KEY }}
good-first-issue-comment:
name: Post good first issue guidance comment
if: >-
github.event.action == 'labeled' && github.event.label.name == 'good first issue'
uses: learningequality/.github/.github/workflows/good-first-issue-comment.yml@main
secrets:
LE_BOT_APP_ID: ${{ secrets.LE_BOT_APP_ID }}
LE_BOT_PRIVATE_KEY: ${{ secrets.LE_BOT_PRIVATE_KEY }}
17 changes: 17 additions & 0 deletions .github/workflows/issue-open.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
name: Handle issue open events
on:
workflow_call:
secrets:
LE_BOT_APP_ID:
description: 'GitHub App ID for authentication'
required: true
LE_BOT_PRIVATE_KEY:
description: 'GitHub App Private Key for authentication'
required: true
jobs:
manage-issue-header:
name: Manage issue header
uses: learningequality/.github/.github/workflows/manage-issue-header.yml@main
secrets:
LE_BOT_APP_ID: ${{ secrets.LE_BOT_APP_ID }}
LE_BOT_PRIVATE_KEY: ${{ secrets.LE_BOT_PRIVATE_KEY }}
5 changes: 0 additions & 5 deletions .github/workflows/manage-issue-header.yml
Comment thread
MisRob marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,6 @@ on:
jobs:
manage-issue-header:
runs-on: ubuntu-latest
if: |
github.event.action == 'opened' ||
github.event.action == 'reopened' ||
(github.event.action == 'labeled' && github.event.label.name == 'help wanted') ||
(github.event.action == 'unlabeled' && github.event.label.name == 'help wanted')
steps:
- name: Generate GitHub token
id: generate-token
Expand Down
60 changes: 49 additions & 11 deletions docs/community-automations.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,61 @@
Manages GitHub issue comments. Sends Slack notifications and GitHub bot replies.

| Contributor type | Issue type | Comment type | #support-dev | #support-dev-notifications | GitHub bot | GitHub bot message |
|------------------|------------|--------------|--------------|---------------------------|------------------|-------------|
|------------------|------------|--------------|--------------|---------------------------|------------|-------------------|
| **Core team** | Any | Any | No | No | No | - |
| **Close contributor** | Any | Any | **Yes** | No | No | - |
| **Issue creator** | `help-wanted` | Any | **Yes** | No | No | - |
| **Issue creator** | Private | Any | No | Yes | No | - |
| **Other** | Private | Regular | No | Yes | No | - |
| **Other** | Private | Assignment request | No | Yes | Yes`*` | `BOT_MESSAGE_ISSUE_NOT_OPEN` |
| **Other** | Unassigned `help-wanted` | Any | **Yes** | No | No | - |
| **Other** | `help-wanted` assigned to the comment author | Any | **Yes** | No | No | - |
| **Other** | `help-wanted` assigned to someone else | Regular | No | Yes | No | - |
| **Other** | `help-wanted` assigned to someone else | Assignment request | No | Yes | Yes`*` | `BOT_MESSAGE_ALREADY_ASSIGNED` |
| **Close contributor** | Any | regular, [assign keyword](https://github.com/learningequality/.github/blob/main/scripts/constants.js#L44) | **Yes** | No | No | - |
| **Issue creator** | `help-wanted` | regular, [assign keyword](https://github.com/learningequality/.github/blob/main/scripts/constants.js#L44) | **Yes** | No | No | - |
| **Issue creator** | Private | regular, [assign keyword](https://github.com/learningequality/.github/blob/main/scripts/constants.js#L44) | No | Yes | No | - |
| **Other** | Private | regular | No | Yes | No | - |
| **Other** | Private | [assign keyword](https://github.com/learningequality/.github/blob/main/scripts/constants.js#L44) | No | Yes | Yes`*` | `BOT_MESSAGE_ISSUE_NOT_OPEN` |
| **Other** | Unassigned `help-wanted` (not good first issue) | regular, [assign keyword](https://github.com/learningequality/.github/blob/main/scripts/constants.js#L44) | **Yes** | No | No | - |
| **Other** | Unassigned `help-wanted` + good first issue | regular | **Yes** | No | No | - |
| **Other** | Unassigned `help-wanted` + good first issue | [assign keyword](https://github.com/learningequality/.github/blob/main/scripts/constants.js#L44) | **Yes** | No | Yes`*` | `BOT_MESSAGE_KEYWORD_GOOD_FIRST_ISSUE` |
| **Other** | `help-wanted` assigned to commenter | regular, [assign keyword](https://github.com/learningequality/.github/blob/main/scripts/constants.js#L44) | **Yes** | No | No | - |
| **Other** | `help-wanted` assigned to someone else | regular | No | Yes | No | - |
| **Other** | `help-wanted` assigned to someone else | [assign keyword](https://github.com/learningequality/.github/blob/main/scripts/constants.js#L44) | No | Yes | Yes`*` | `BOT_MESSAGE_ALREADY_ASSIGNED` |
| **Close contributor** | Private | `/assign` command | No | Yes | Yes | `BOT_MESSAGE_ISSUE_NOT_OPEN` |
| **Close contributor** | `help-wanted` assigned to commenter | `/assign` command | No | No | No | - (silent no-op) |
| **Close contributor** | `help-wanted` assigned to someone else | `/assign` command | No | Yes | Yes | `BOT_MESSAGE_ALREADY_ASSIGNED` |
| **Close contributor** | Unassigned `help-wanted` (not good first issue) | `/assign` command | No | Yes | Yes | `BOT_MESSAGE_ASSIGN_NOT_GOOD_FIRST_ISSUE` |
| **Close contributor** | Unassigned `help-wanted` + good first issue, under limit | `/assign` command | No | Yes | Yes | `BOT_MESSAGE_ASSIGN_SUCCESS` |
| **Close contributor** | Unassigned `help-wanted` + good first issue, at limit | `/assign` command | No | Yes | Yes | Dynamic at-limit message |
| **Issue creator** | Private | `/assign` command | No | Yes | Yes | `BOT_MESSAGE_ISSUE_NOT_OPEN` |
| **Issue creator** | `help-wanted` assigned to commenter | `/assign` command | No | No | No | - (silent no-op) |
| **Issue creator** | `help-wanted` assigned to someone else | `/assign` command | No | Yes | Yes | `BOT_MESSAGE_ALREADY_ASSIGNED` |
| **Issue creator** | Unassigned `help-wanted` (not good first issue) | `/assign` command | No | Yes | Yes | `BOT_MESSAGE_ASSIGN_NOT_GOOD_FIRST_ISSUE` |
| **Issue creator** | Unassigned `help-wanted` + good first issue, under limit | `/assign` command | No | Yes | Yes | `BOT_MESSAGE_ASSIGN_SUCCESS` |
| **Issue creator** | Unassigned `help-wanted` + good first issue, at limit | `/assign` command | No | Yes | Yes | Dynamic at-limit message |
| **Other** | Private | `/assign` command | No | Yes | Yes | `BOT_MESSAGE_ISSUE_NOT_OPEN` |
| **Other** | `help-wanted` assigned to commenter | `/assign` command | No | No | No | - (silent no-op) |
| **Other** | `help-wanted` assigned to someone else | `/assign` command | No | Yes | Yes | `BOT_MESSAGE_ALREADY_ASSIGNED` |
| **Other** | Unassigned `help-wanted` (not good first issue) | `/assign` command | No | Yes | Yes | `BOT_MESSAGE_ASSIGN_NOT_GOOD_FIRST_ISSUE` |
| **Other** | Unassigned `help-wanted` + good first issue, under limit | `/assign` command | No | Yes | Yes | `BOT_MESSAGE_ASSIGN_SUCCESS` |
| **Other** | Unassigned `help-wanted` + good first issue, at limit | `/assign` command | No | Yes | Yes | Dynamic at-limit message |

`*` There is an additional optimization that prevents more than one bot message per hour to not overwhelm issue comment section

In `scripts/contants.js` set:
**`/assign` command** applies to all external contributors (close contributors, issue creators, and others). Core team members never reach the script. `/assign` must be the entire comment (trimmed, case-insensitive). All `/assign` Slack activity goes to `#support-dev-notifications` only.

**`/assign` cross-repo limit:** Contributors can have up to 2 assigned issues across all community repos (`COMMUNITY_REPOS`). Issues unassigned within the last 7 days count toward the limit (`currentAssignments + recentUnassignments >= MAX_ASSIGNED_ISSUES`).

In `scripts/constants.js` set:
- `BOT_MESSAGE_ISSUE_NOT_OPEN`: _Issue not open for contribution_ message text
- `BOT_MESSAGE_ALREADY_ASSIGNED`: _Issue already assigned_ message text
- `BOT_MESSAGE_ASSIGN_SUCCESS`: Assignment confirmation message
- `BOT_MESSAGE_ASSIGN_NOT_GOOD_FIRST_ISSUE`: Decline message for non-good-first-issue issues
- `BOT_MESSAGE_KEYWORD_GOOD_FIRST_ISSUE`: Keyword reply with `/assign` guidance

# `good-first-issue-comment`

Posts a guidance comment when the `good first issue` label is applied to an issue (triggered via the `issue-label` workflow). Explains the `/assign` command, issue limits, cooldown, and links to contributing guidelines.

- Only posts if the issue also has `help wanted` label
- Deletes any previous guidance comment from the bot before posting a new one
- Identified by the `<!-- ASSIGN_GUIDANCE -->` HTML comment marker

In `scripts/constants.js` set:
- `BOT_MESSAGE_GOOD_FIRST_ISSUE_GUIDANCE`: Guidance message text

# `contributor-pr-reply`

Expand Down
60 changes: 59 additions & 1 deletion scripts/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,10 @@ const KEYWORDS_DETECT_ASSIGNMENT_REQUEST = [
];

const ISSUE_LABEL_HELP_WANTED = 'help wanted';
const ISSUE_LABEL_GOOD_FIRST_ISSUE = 'good first issue';
const MAX_ASSIGNED_ISSUES = 2;
const ASSIGN_COOLDOWN_DAYS = 7;
const ASSIGN_GUIDANCE_MARKER = '<!-- ASSIGN_GUIDANCE -->';
const LABEL_COMMUNITY_REVIEW = 'community-review';

// Will be attached to bot messages when not empty
Expand All @@ -92,7 +96,52 @@ const BOT_MESSAGE_ISSUE_NOT_OPEN = `Hi! 👋 \n\n Thanks so much for your intere

const BOT_MESSAGE_ALREADY_ASSIGNED = `Hi! 👋 \n\n Thanks so much for your interest! **This issue is already assigned. Visit [Contributing guidelines](https://learningequality.org/contributing-to-our-open-code-base) to learn about the contributing process and how to find suitable issues. If there are no unassigned 'help wanted' issues available, please wait until new ones are added.** \n\n We really appreciate your willingness to help. 😊${GSOC_NOTE}`;

const BOT_MESSAGE_PULL_REQUEST = (author) => `👋 Hi @${author}, thanks for contributing! \n\n **For the review process to begin, please verify that the following is satisfied:**\n\n- [ ] **Contribution is aligned with our [contributing guidelines](https://learningequality.org/contributing-to-our-open-code-base)**\n- [ ] **Pull request description has correctly filled _AI usage_ section & follows our AI guidance:**\n\n <details>\n <summary><b><i>AI guidance</i></b></summary>\n\n <br>\n\n **State explicitly whether you didn't use or used AI & how.**\n\n If you used it, ensure that the PR is aligned with [Using AI](https://learningequality.org/contributing-to-our-open-code-base/#using-generative-ai) as well as our DEEP framework. DEEP asks you:\n\n - **Disclose** — Be open about when you've used AI for support.\n - **Engage critically** — Question what is generated. Review code for correctness and unnecessary complexity.\n - **Edit** — Review and refine AI output. Remove unnecessary code and verify it still works after your edits.\n - **Process sharing** — Explain how you used the AI so others can learn.\n\n <br>\n\n Examples of good disclosures:\n\n > "I used Claude Code to implement the component, prompting it to follow the pattern in ComponentX. I reviewed the generated code, removed unnecessary error handling, and verified the tests pass."\n\n > "I brainstormed the approach with Gemini, then had it write failing tests for the feature. After reviewing the tests, I used Claude Code to generate the implementation. I refactored the output to reduce verbosity and ran the full test suite."\n\n </details>\n\nAlso check that issue requirements are satisfied & you ran \`pre-commit\` locally. \n\n**Pull requests that don't follow the guidelines will be closed.**\n\n**Reviewer assignment can take up to 2 weeks.**`;
const BOT_MESSAGE_GOOD_FIRST_ISSUE_GUIDANCE =
`${ASSIGN_GUIDANCE_MARKER}\n\n` +
`Hi! 👋\n\n` +
`This issue is available for contribution and supports ` +
`**self-assignment**. Here's how to get started:\n\n` +
`- **Comment \`/assign\` to assign yourself** to this issue\n` +
`- You can have up to **${MAX_ASSIGNED_ISSUES} issues** assigned ` +
`at a time across all community repos\n` +
`- Dropping an issue has a **${ASSIGN_COOLDOWN_DAYS}-day cooldown** ` +
`before the slot opens up\n` +
`- **Link your pull request** to this issue when you submit it` +
`\n\n📖 **Read the [Contributing guidelines]` +
`(https://learningequality.org/contributing-to-our-open-code-base/)` +
` before starting.**${GSOC_NOTE}`;

const BOT_MESSAGE_ASSIGN_SUCCESS =
`Hi! 👋\n\n` +
`You've been assigned to this issue. Here's what to do next:\n\n` +
`- **Read the issue description** carefully and make sure ` +
`you understand the requirements\n` +
`- **Link your pull request** to this issue when you submit it\n` +
`- If you can no longer work on this, **unassign yourself** ` +
`so others can pick it up\n\n` +
`Good luck! 😊${GSOC_NOTE}`;

const BOT_MESSAGE_ASSIGN_NOT_GOOD_FIRST_ISSUE =
`Hi! 👋\n\n` +
`Self-assignment via \`/assign\` is only available for issues ` +
`labeled **\`good first issue\`**. This issue does not have ` +
`that label.\n\n` +
`Visit [Contributing guidelines]` +
`(https://learningequality.org/contributing-to-our-open-code-base/)` +
` to learn about the contributing process and how to find ` +
`suitable issues. 😊${GSOC_NOTE}`;

const BOT_MESSAGE_KEYWORD_GOOD_FIRST_ISSUE =
`Hi! 👋\n\n` +
`Thanks for your interest! This issue supports ` +
`**self-assignment**. **Comment \`/assign\` to assign ` +
`yourself.**\n\n` +
`Visit [Contributing guidelines]` +
`(https://learningequality.org/contributing-to-our-open-code-base/)` +
` to learn about the contributing process. 😊${GSOC_NOTE}`;

const BOT_MESSAGE_PULL_REQUEST = author =>
`👋 Hi @${author}, thanks for contributing! \n\n **For the review process to begin, please verify that the following is satisfied:**\n\n- [ ] **Contribution is aligned with our [contributing guidelines](https://learningequality.org/contributing-to-our-open-code-base)**\n- [ ] **Pull request description has correctly filled _AI usage_ section & follows our AI guidance:**\n\n <details>\n <summary><b><i>AI guidance</i></b></summary>\n\n <br>\n\n **State explicitly whether you didn't use or used AI & how.**\n\n If you used it, ensure that the PR is aligned with [Using AI](https://learningequality.org/contributing-to-our-open-code-base/#using-generative-ai) as well as our DEEP framework. DEEP asks you:\n\n - **Disclose** — Be open about when you've used AI for support.\n - **Engage critically** — Question what is generated. Review code for correctness and unnecessary complexity.\n - **Edit** — Review and refine AI output. Remove unnecessary code and verify it still works after your edits.\n - **Process sharing** — Explain how you used the AI so others can learn.\n\n <br>\n\n Examples of good disclosures:\n\n > "I used Claude Code to implement the component, prompting it to follow the pattern in ComponentX. I reviewed the generated code, removed unnecessary error handling, and verified the tests pass."\n\n > "I brainstormed the approach with Gemini, then had it write failing tests for the feature. After reviewing the tests, I used Claude Code to generate the implementation. I refactored the output to reduce verbosity and ran the full test suite."\n\n </details>\n\nAlso check that issue requirements are satisfied & you ran \`pre-commit\` locally. \n\n**Pull requests that don't follow the guidelines will be closed.**\n\n**Reviewer assignment can take up to 2 weeks.**`;

const HOLIDAY_MESSAGE = `Season's greetings! 👋 \n\n We'd like to thank everyone for another year of fruitful collaborations, engaging discussions, and for the continued support of our work. **Learning Equality will be on holidays from December 22 to January 5.** We look forward to much more in the new year and wish you a very happy holiday season!${GSOC_NOTE}`;

Expand All @@ -119,8 +168,17 @@ module.exports = {
CLOSE_CONTRIBUTORS,
KEYWORDS_DETECT_ASSIGNMENT_REQUEST,
ISSUE_LABEL_HELP_WANTED,
GSOC_NOTE,
ISSUE_LABEL_GOOD_FIRST_ISSUE,
MAX_ASSIGNED_ISSUES,
ASSIGN_COOLDOWN_DAYS,
ASSIGN_GUIDANCE_MARKER,
BOT_MESSAGE_ISSUE_NOT_OPEN,
BOT_MESSAGE_ALREADY_ASSIGNED,
BOT_MESSAGE_GOOD_FIRST_ISSUE_GUIDANCE,
BOT_MESSAGE_ASSIGN_SUCCESS,
BOT_MESSAGE_ASSIGN_NOT_GOOD_FIRST_ISSUE,
BOT_MESSAGE_KEYWORD_GOOD_FIRST_ISSUE,
BOT_MESSAGE_PULL_REQUEST,
BOT_MESSAGE_RTIBBLESBOT_REVIEW,
RTIBBLESBOT_USERNAME,
Expand Down
Loading
Loading