Skip to content

feat: add the /meow command - #104

Open
thc1006 wants to merge 1 commit into
cncf:mainfrom
thc1006:feat/meow-command
Open

feat: add the /meow command#104
thc1006 wants to merge 1 commit into
cncf:mainfrom
thc1006:feat/meow-command

Conversation

@thc1006

@thc1006 thc1006 commented Aug 25, 2026

Copy link
Copy Markdown

Description

Adds a /meow command that replies with a random cat image, requested in #78.

It matches only a standalone /meow line, so /meowvie (a real upstream command) and prose or urls that merely mention /meow do not trigger it. The image is fetched from the cat API with the global fetch, so it adds no dependencies, and is posted through the existing createComment helper, following the shape of the other issue comment commands.

Closes #78

Behavior

  • Exact command match. The central dispatcher matches commands by substring, so /meow alone would also fire on /meowvie, /meow-debug, and text like please run /meow. The command guards itself with /^[\t ]*\/meow[\t ]*$/m and returns early otherwise. Reworking the shared command parser is out of scope here.
  • Bounded external call. Each attempt has a 5s AbortSignal.timeout and the command retries at most three times, only for a network failure or a 5xx. Timeout errors, 429 responses, other 4xx responses, invalid responses, and the GitHub write are not retried; retryable network failures and 5xx responses can each consume up to their own per-attempt deadline, so the total wall-clock time is bounded by the attempt count rather than a single deadline. A 429 is a rate limit, so it falls back rather than sending the same request again. Every non-2xx response has its body cancelled before the retry or fallback, so undici releases the connection instead of leaving it for the garbage collector.
  • Validated response. parseCatImage checks the payload is a non-empty array whose first url is a string, bounds its length, parses it with new URL, and requires https with no embedded credentials. The url is embedded with the angle bracket form ![cat](<...>) on the normalized href, so a parenthesis or other reserved character in a valid url renders correctly and an unexpected response cannot break out of the image.
  • Graceful degradation. /meow is enabled per repository and open to anyone, so a cat API outage should not turn a repository's workflow red. A failed fetch logs core.warning and leaves a short note; only a failed GitHub comment write fails the Action.
  • Optional API key. An optional cat-api-key input is sent as the x-api-key header when set, with redirect: 'manual' so a redirect is refused rather than followed and the header cannot reach another origin. Anonymous requests are best effort and share a rate limit. The key comes from a repository secret and is registered with core.setSecret for runner masking before the request, and is never intentionally included in the request URL or a GitHub comment.

Scope

This intentionally implements only the bare /meow command. /meowvie and category arguments (/meow <category>) remain out of scope. The exact matching is a correctness fix rather than an enhancement, since without it /meowvie is wrongly executed as /meow. Command detection otherwise inherits the existing substring dispatcher, so a /meow line inside a code fence is still matched like any other command; reworking that shared parser is out of scope here. Issue #66 tracks ignoring code blocks and #81 tracks multi-command parsing behavior, and both apply to every command rather than being specific to /meow, so this change does not close either. Because /meow calls a third party and anyone can invoke it, it is kept out of the default Quickstart command list and documented as an opt-in command with its own example in the commands docs; enabling it in this repository's own workflow is left as a follow-up for the maintainers. This touches the same dispatch switch as #103, so if #103 merges first this rebases onto it, keeping every .catch(normalizeError) and adding the /meow case, with dist/index.js regenerated from a clean install rather than hand-merged. That regeneration also brings the bundled transitive brace-expansion in sync with the unchanged lockfile; no dependency versions changed.

Testing

  • npm run all passes (build, lint, pack, test): 106 passing, 6 skipped
  • meow.test.ts runs with onUnhandledRequest: 'error' so a missed mock cannot reach the live cat API or GitHub, and spies createComment so no real write escapes
  • covers a standalone /meow, a url with parentheses, the four forms that must not trigger it (/meowvie, /meow-debug, /meow cat, prose), a CRLF comment where the standalone /meow still matches exactly once, a transient retry then success, a network error retried to the attempt limit, a persistent failure over three attempts, a rate limit that is not retried, a timeout that is not retried, an empty response, a first item without a url, an unparseable url, an over-length url, a non-https url, a credential url, a refused redirect that is not followed, a response body cancelled on both a retry and a non-retry, the request query, the API key header both set and absent, the key registered for masking, and a failed GitHub write that fails the Action
  • confirmed those four cases fail when the standalone guard is removed

Checklist:

  • I ran npm run all to lint and build my code
  • No new dependencies
  • I have performed a self review of my own code
  • I have commented my code, particularly in hard to understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings

@thc1006
thc1006 requested a review from jpmcb as a code owner August 25, 2026 13:24
@thc1006
thc1006 force-pushed the feat/meow-command branch 7 times, most recently from 9c7225c to 93f4346 Compare August 26, 2026 16:27
Adds a /meow command that replies with a random cat image (cncf#78). It
matches only a standalone /meow line, so /meowvie and prose mentioning
/meow do not trigger it. The image is fetched from the cat api with the
global fetch (no new dependency) under a request timeout and a small
bounded retry, the response is validated, and the url is embedded with
the angle bracket markdown form. A cat api outage degrades to a short
note rather than failing the workflow; only the github write fails the
action. An optional cat-api-key input is sent as the x-api-key header
when set.

Signed-off-by: thc1006 <84045975+thc1006@users.noreply.github.com>
@thc1006
thc1006 force-pushed the feat/meow-command branch from 93f4346 to deb0b50 Compare August 26, 2026 16:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add meow command

1 participant