Conversation
`parseDurationInMilliseconds()` was introduced in commit acdc390 ("Convert the Action to Javascript", 2021-03-20) with three identical regular expressions where three different ones were intended: const milliSeconds = text.match(/(\d+)\s*m/) if (milliSeconds) ms += parseInt(milliSeconds[1]) const seconds = text.match(/(\d+)\s*m/) if (seconds) ms += parseInt(seconds[1]) * 1000 const minutes = text.match(/(\d+)\s*m/) if (minutes) ms += parseInt(minutes[1]) * 60000 The unit characters in the patterns above were meant to be `ms`, `s`, and `m` respectively, matching the suffixes on the action's `max-age` input. Because all three patterns matched the same substring (`\d+\s*m`), three things were wrong: - `30s` returned 0, because no pattern matched (`30s` has no `m`). Any user passing a `max-age` in seconds therefore had every feed item filtered out as "too old": the limit time became `Date.now()` itself. - `30m` returned 1,830,030 instead of 1,800,000, because the same `\d+\s*m` substring was counted three times: once as milliseconds (30), once as seconds (30 * 1000), and once as minutes (30 * 60000). The outcome was a ~1.7% overshoot, so the action accepted items up to ~30 seconds older than it should have. - `30ms` returned 1,830,030 (~30.5 minutes) for the same overcounting reason, an ~61,000x overshoot. The two units that work correctly in the existing implementation (`h` and `d`) are exactly the two that the existing tests exercise: `48h` and `9999d`. The bug therefore went undetected by CI for the entire lifetime of the JavaScript port. Fix by giving each unit its own regex with a negative lookahead so that the unit characters do not overlap: /(\d+)\s*ms/ -> matches "30ms" /(\d+)\s*s(?!\w)/ -> matches "30s" but not the "s" inside "30ms" /(\d+)\s*m(?!s)/ -> matches "30m" but not the "m" inside "30ms" The `s(?!\w)` guard prevents seconds from being matched inside plain English words like "30 minutes" (where the `s` of "minutes" would otherwise count). The `m(?!s)` guard prevents minutes from being matched inside `ms`. The `h` and `d` patterns are unchanged because their unit characters do not collide with anything else the function recognises. Verified outputs of the fixed function: 30ms -> 30 ms 30s -> 30,000 ms 30m -> 1,800,000 ms 30h -> 108,000,000 ms 30d -> 2,592,000,000 ms 1h30m -> 5,400,000 ms (the function was already designed to accept compound forms) 1d2h -> 93,600,000 ms Add a regression test that pins the seconds case: an item published 5 seconds ago with `max-age: 30s` must be kept. With the buggy regex the test fails because the item is silently filtered out (`max-age` parses to 0 ms); with the fixed regex it passes. The new test resets `core.__INPUTS__` first because earlier tests in the file mutate it without cleaning up. Assisted-by: Opus 4.7 Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
The "skip this item because its content does not match `content-pattern`" branch in `run()` has called a misspelled `core.debug$(...)` ever since the JavaScript port (commit acdc390, 2021-03-20). The other five `core.debug(...)` calls in the same function are spelled correctly; only this one carries a stray `$`, introduced presumably as a finger slip while retyping the equivalent log line from the deleted Go original. What kept this from being noticed: - ESLint's `no-undef` checks identifiers, not member accesses; `core.debug$` is a syntactically valid (dynamic) property lookup whose value just happens to be `undefined`. The crash only occurs when the property is then *called*, which is at runtime. - The bug only triggers when a workflow sets the action's `content-pattern` input AND a feed item's content fails the pattern. The existing test suite never set `content-pattern`, so the failing branch was never exercised in CI. When the branch does fire, `run()` rejects with `TypeError: core.debug$ is not a function`, the action's runtime catch translates that into `core.setFailed(e.message)`, and the workflow step fails with a confusing error message. Fix by removing the stray `$`, and add a regression test that configures `content-pattern` against a two-entry feed where one entry matches and one does not. The test asserts that exactly one issue is created (the matching one), which is only true once the typo is gone: with the typo present, processing the second entry crashes before any assertions can run. Assisted-by: Opus 4.7 Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Assisted-by: Opus 4.7 Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
rimrul
reviewed
May 29, 2026
rimrul
left a comment
Member
There was a problem hiding this comment.
The
s(?!\w)guard prevents seconds from being matched inside
plain English words like "30 minutes" (where thesof "minutes"
would otherwise count).
This example in the ca70045 commit message is an odd choice. minutes wouldn't match because e is neither numeric nor whitespace, not because of the '(?!\w)'
| <published>${date}</published> | ||
| <content type="html">x</content> | ||
| </entry> | ||
| </feed>` |
Member
There was a problem hiding this comment.
shouldn't the test include at least one entry on either side of the limit to effectively test that the limit is correctly interpreted? at the moment we just test that '30s' isn't interpreted as a vastly to small time span, but it could in theory still be 30 days or even no limit, for all this test catches.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Two long-standing bugs found while reviewing the codebase in preparation for the imminent Node.js 24 / ESM migration. Both have been in
index.jssince acdc390 ("Convert the Action to Javascript", 2021-03-20), neither is caught by ESLint, and neither is exercised by the existing test suite, so they have lived undetected through five years of releases.core.debug$typo on thecontent-patternskip branchThe other five
core.debug(...)calls inrun()are spelled correctly; only this one carries the stray$. Effect: any workflowthat uses the
content-patterninput crashes withTypeError: core.debug$ is not a functionthe moment a feed item fails the pattern;core.setFailedtranslates the rejection into a red workflow step.parseDurationInMilliseconds()triplet regexThe function declares three regex variables (
milliSeconds,seconds,minutes) all bound to the same pattern/(\d+)\s*m/, where/(\d+)\s*ms/,/(\d+)\s*s(?!\w)/, and/(\d+)\s*m(?!s)/were intended. Effect:30ms30s30m30h30dOnly
handdhappen to land on correct values, and the existing tests use exactly48hand9999d, which is why CI never noticed. A user passingmax-age: 30sgot every feed item filtered out silently.What this PR does
Two source commits, each followed by an
npm run prepareper repo convention:(
s(?!\w),m(?!s)). New regression testrespects max-age when expressed in secondspins the seconds case; verified to fail on the buggy code and pass on the fixed code. Verified outputs:30ms→30,30s→30000,30m→1,800,000,30h→108,000,000,30d→2,592,000,000,1h30m→5,400,000,1d2h→93,600,000.core.debug$typo. New regression testrespects content-pattern filterconfigurescontent-patternagainst a two-entry feed where one entry matches and one does not; verified to fail (with the actualTypeError: core.debug$ is not a function) on the buggy code and pass on the fixed code.