Repository navigation
feat: support discussion_comment event #101
Description
Summary
Extend command-action to support the discussion_comment event in addition to issue_comment, so IssueOps commands can be triggered from comments on GitHub Discussions.
To stay aligned with the I/F finalized in #100, this change extends the same output vocabulary (number, context) and the allowed_contexts input vocabulary to cover discussions.
Note
This issue depends on #100. Implement it after the number / context output redesign in #100 has landed, and base the new behavior on that redesigned surface.
Motivation
command-actioncurrently only handlesissue_commentand emits a warning for any othereventName(src/main.ts:11-14). Workflows triggered byon: discussion_commentare dropped without doing any parsing.- GitHub Discussions are a first-class collaboration surface in many repositories, and IssueOps-style commands (
.deploy,.preview,.label, etc.) are equally useful in discussion threads (Q&A triage, RFC voting, release-note drafting, etc.). - The I/F redesign in feat(output): add
numberandcontextoutputs, soft-deprecateissue_number#100 already moves the output vocabulary toward an event-agnostic shape (number/context). Extending it once more to cover discussions is cheap if it ships right after feat(output): addnumberandcontextoutputs, soft-deprecateissue_number#100, and avoids a third migration later.
Proposed changes
1. Accept discussion_comment in isValidContext
src/main.ts:11-14currently rejects any event other thanissue_comment. Replace the equality check with an allow-list of{"issue_comment", "discussion_comment"}.- Keep the warning message for unsupported events but list both accepted events.
2. Extend allowed_contexts vocabulary
- Add
discussionto the allowed values ofallowed_contexts(currentlyissue/pull_request). - Update the
validContextsset insrc/main.ts:8to{"issue", "pull_request", "discussion"}. - Update the default of
allowed_contextsinaction.yaml:14fromissue,pull_requesttoissue,pull_request,discussion. This is a strict addition: workflows that do not listen todiscussion_commentare unaffected, and workflows that do gain support without manual opt-in.
3. Determine the triggering context
Introduce a single helper that, given the current @actions/github context, returns the discriminated triple { kind: "issue" | "pull_request" | "discussion", number, commentId, actor, body }. Use it from both isValidContext (for the allowed_contexts filter) and the output emission site, so the kind is computed in exactly one place.
Determination rules:
| Event | Condition | kind |
|---|---|---|
issue_comment |
payload.issue.pull_request != null |
"pull_request" |
issue_comment |
otherwise | "issue" |
discussion_comment |
always | "discussion" |
This replaces the existing isPr boolean in src/main.ts:26 (which #100 already plans to lift out — coordinate so both changes converge on the same helper rather than introducing two competing abstractions).
4. Outputs on the discussion context
After isValidContext passes, emit the following for a discussion kind:
| Output | Value | Notes |
|---|---|---|
number |
payload.discussion.number |
Same semantics as #100's number for issue/PR. |
context |
"discussion" |
Extends #100's `"issue" |
comment_id |
payload.comment.id |
Same payload shape as issue_comment. |
actor |
payload.comment.user.login |
Same payload shape as issue_comment. |
command / params / continue |
Per existing logic | Comment body parsing is event-agnostic. |
issue_number |
not emitted | The legacy name does not apply to discussions; downstream consumers should migrate to number (per #100's deprecation). For issue_comment events, issue_number continues to be emitted unchanged. |
5. Parser reuse
parse(body)insrc/parse.tsalready operates on a plain comment body string. No parser changes required.- Confirm via a unit test on
main.tsthat a discussion comment body flows through the same parsing path.
6. README updates
- Update §Inputs / §Outputs tables (regenerated by
pnpm generate):allowed_contextsdefault changes toissue,pull_request,discussion.- Description adds
discussionas an allowed value. contextoutput description lists the three possible values.
- Add a §Getting Started example for discussions:
on: discussion_comment: types: [created]permissions: discussions: write(required for adding reactions to discussion comments viagithub.graphql—addReactionmutation; same level of detail as the existing Tips section for issue/PR reactions).- Brief callout linking to
allowed_contextsif the workflow wants to restrict to discussions only.
7. Tests
src/main.ts currently has no unit tests. Add src/main.test.ts (vitest) covering the matrix below. Use vi.mock("@actions/github", ...) to inject eventName and payload per case, and spy on core.setOutput / core.warning to assert emission.
| Case | Event | Payload shape | Expected |
|---|---|---|---|
| Issue comment | issue_comment |
payload.issue.pull_request == null |
context="issue", number=payload.issue.number, issue_number emitted |
| PR comment | issue_comment |
payload.issue.pull_request != null |
context="pull_request", number=payload.issue.number, issue_number emitted |
| Discussion comment | discussion_comment |
payload.discussion.number set |
context="discussion", number=payload.discussion.number, issue_number not emitted |
| Unsupported event | e.g., push |
n/a | warning emitted, continue="false", no other outputs |
allowed_contexts filter mismatch |
discussion_comment with allowed_contexts="issue" |
discussion payload | info emitted, continue="false" |
Spec details
action.yaml
inputs:
allowed_contexts:
description: 'The comment contexts that trigger the IssueOps command, specified as a comma-separated list. Allowed values: "issue", "pull_request", "discussion".'
default: 'issue,pull_request,discussion'
outputs:
context:
description: 'The context that triggered this action. One of "issue", "pull_request", or "discussion".'
# (other outputs unchanged from #100)src/main.ts (sketch)
const validContexts = new Set(['issue', 'pull_request', 'discussion'] as const);
type CommentKind = 'issue' | 'pull_request' | 'discussion';
const resolveCommentContext = (): {
kind: CommentKind;
number: number;
commentId: number;
actor: string;
body: string;
} | null => {
if (context.eventName === 'issue_comment') {
const isPr = context.payload.issue?.pull_request != null;
return {
kind: isPr ? 'pull_request' : 'issue',
number: context.payload.issue!.number!,
commentId: context.payload.comment!.id,
actor: context.payload.comment!.user.login,
body: (context.payload.comment?.body ?? '') as string,
};
}
if (context.eventName === 'discussion_comment') {
return {
kind: 'discussion',
number: context.payload.discussion!.number,
commentId: context.payload.comment!.id,
actor: context.payload.comment!.user.login,
body: (context.payload.comment?.body ?? '') as string,
};
}
return null;
};Output emission inside run then becomes:
core.setOutput('number', ctx.number);
core.setOutput('context', ctx.kind);
core.setOutput('comment_id', ctx.commentId);
core.setOutput('actor', ctx.actor);
if (ctx.kind !== 'discussion') {
core.setOutput('issue_number', ctx.number);
}Acceptance criteria
-
action.yamldeclaresallowed_contextsdefaultissue,pull_request,discussionand listsdiscussionin the description. -
action.yaml'scontextoutput description lists"issue","pull_request","discussion". -
src/main.tsacceptseventNameof eitherissue_commentordiscussion_commentand rejects others with a warning. - A single helper computes
{ kind, number, commentId, actor, body }and is used by both theallowed_contextsfilter and the output emission. - For
discussion_comment:number === payload.discussion.number,context === "discussion",issue_numberis not emitted. - For
issue_comment(issue or PR): behavior matches feat(output): addnumberandcontextoutputs, soft-deprecateissue_number#100 (number === payload.issue.number,issue_numberstill emitted). - When
allowed_contextsdoes not includediscussion, adiscussion_commentevent setscontinue="false"and emits an info message, without settingnumber/context/ etc. -
src/main.test.tscovers the five cases in the test matrix above. - README §Inputs / §Outputs tables are regenerated via
pnpm generateand reflect the new default anddiscussionvalue. - README has a discussion_comment example workflow including
permissions: discussions: write. -
pnpm buildproduces an updateddist/, committed in the same PR. -
pnpm typecheck,pnpm lint,pnpm test,pnpm generate(no drift) all pass in CI.
Out of scope
- Filtering by Discussion category (
Q&A,General,Announcements, etc.). If demand surfaces, design a separate input (e.g.allowed_discussion_categories) in a follow-up issue. - Answering / marking discussion comments as the chosen answer. Belongs to user-side workflow logic via the GraphQL API, not this action.
- Reaction support. Already handled by the README Tips section pattern and out of scope for this action.
- Removal of
issue_number. Tracked separately under feat(output): addnumberandcontextoutputs, soft-deprecateissue_number#100's "next major release" plan. - Locked / archived discussion handling. GitHub already prevents new comments in those states, so no special handling needed here.
Migration
For existing v1 consumers, no migration is required:
- Workflows that only listen to
issue_commentare unaffected. - Workflows that want to support discussions add
on: discussion_comment: types: [created]andpermissions: discussions: write, and switch fromoutputs.issue_numbertooutputs.number(already recommended by feat(output): addnumberandcontextoutputs, soft-deprecateissue_number#100).
on:
issue_comment:
types: [created]
discussion_comment:
types: [created]
permissions:
contents: read
issues: write
pull-requests: write
discussions: write
jobs:
demo:
runs-on: ubuntu-latest
steps:
- id: command
uses: knowledge-work/command-action@v1
with:
command: 'greet'
- if: ${{ steps.command.outputs.continue == 'true' }}
run: |
echo "context=${{ steps.command.outputs.context }}"
echo "number=${{ steps.command.outputs.number }}"
echo "name=${{ fromJSON(steps.command.outputs.params).name }}"Assumptions
- The discussion_comment payload exposes
discussion.number,comment.id,comment.body, andcomment.user.loginin the same shape as documented in GitHub Webhooks docs. Verify against@actions/github'sWebhookPayloadtypings during implementation; if the field shape differs, adjust the helper accordingly. - Expanding the
allowed_contextsdefault to includediscussionis treated as a non-breaking addition because the action still requires the workflow'son:trigger to firediscussion_commentfor the new path to activate. - Naming the new context value
"discussion"(singular, matchingallowed_contexts) is consistent with the existing"issue"/"pull_request"vocabulary in feat(output): addnumberandcontextoutputs, soft-deprecateissue_number#100.
References
- feat(output): add
numberandcontextoutputs, soft-deprecateissue_number#100 — output redesign that introducesnumberandcontext. This issue extends that vocabulary. src/main.ts:8—validContextsset.src/main.ts:11-14— current eventName rejection.src/main.ts:26— currentisPrcomputation.src/main.ts:58-60— current output emission site.action.yaml:14— currentallowed_contextsdefault.action.yaml:25-28— current outputs declaration.- GitHub Webhooks:
discussion_comment.
Activity
github-actions commentedon May 16, 2026
🎉 This issue has been resolved in version 2.2.0 🎉
The release is available on GitHub release
Your semantic-release bot 📦🚀