fix: paginate release lookup and stop dereferencing a null previous release (#10) @goruha (#11)
## Summary- Fixes a deterministic crash on any release that is not on the first page of
listReleases— the action dies withTypeError: Cannot read properties of null (reading 'tag_name'), which buries the accurate error message it emits one line earlier. - Fixes previous-release selection ignoring
include_regex, which in a monorepo with per-module tags silently comments on pull requests from an unrelated module.
Two reproductions in DriveWealth/dw-cc-posting-service (private, 82 releases):
| run | tag | position in listReleases |
|---|---|---|
33548407262 |
dw-cc-posting-service-database@0.1.0 |
index 66 of 82 |
32513890189 |
dw-cc-ps-posting-service-database@0.1.0 |
index 42 of 82 |
Both fail identically. Run 32513890189 was run_attempt: 2 — the re-run failed the same way, so this is not a transient API issue:
This action requires that at least one release prior to <tag> exists, regardless of "include_regex".
Unhandled error: TypeError: Cannot read properties of null (reading 'tag_name')
Changes
Two commits, deliberately separable.
1. listReleases pagination + return after setFailed
github.rest.repos.listReleases was called without pagination, so it returned at most 30 releases. GitHub orders releases by creation date descending, and a release published from a draft keeps the draft's creation date — so a draft cut weeks ago and published today lands deep in the list rather than at the top. When the current release is not on that first page, the loop never sets currentReleaseFound and previousRelease stays null.
core.setFailed does not halt execution, so compareCommits({ base: previousRelease.tag_name }) then ran against null. The resulting TypeError is what surfaces in the Actions UI, masking the actionable message.
- Paginate via
github.paginate(github.rest.repos.listReleases, { …, per_page: 100 }).paginatereturns the array directly, so the loop iteratesreleasesrather thanreleases.data. returnaftercore.setFailed(...)so the real message is the failure.
2. Prefer a previous release that also matches include_regex
Separable — feel free to drop this commit; commit 1 stands alone.
Previous-release selection ignored include_regex, so in a monorepo the base is usually an unrelated module. Traced for
dw-cc-ps-posting-service-database@0.1.0: index 43 (dw-cc-ps-margin-data@0.1.0) is skipped by the existing same-SHA guard, so the base became index 44,
dw-cc-ps-spring-boot-starter@0.1.0-rc.1. The action then compares a database release against a spring-boot-starter RC and comments on whatever pull requests fall in that arbitrary range — wrong PRs, silently, with no error.
This commit prefers the first eligible release whose tag also matches the already-compiled include_regex pattern. The existing same-SHA guard (the "test scenarios" comment block) is untouched and still evaluated first.
Backward compatibility:
- With the default
include_regexof.*this is a no-op — every tag matches. - When
include_regexmatches no prior release at all, it emits acore.warningand falls back to exactly the release that would have been chosen before. That keeps working for a repo cutting a module's first release — and, importantly, for this repo's owntest-positive.yml, whosev0.0.0-test.include.*prereleases are deleted by the teardown job with--cleanup-tag. At run time no prior release matches that workflow'sinclude_regex, so without the fallback the positive test would start failing.
Testing
There is no JS test harness in this repo, so I extracted the inline script: block from action.yml and executed it against stubbed github / core / context objects with an 82-release fixture mirroring the ordering above. The listReleases stub deliberately returns only the first page, so any un-paginated call still reproduces the bug. Assertions are on the base argument that actually reaches compareCommits.
Run against v0.2.0 (e823005), then after each commit:
| case | v0.2.0 | + commit 1 | + commit 2 |
|---|---|---|---|
| tag at index 66, per-module regex | TypeError |
dw-cc-ps-events@0.15.0 |
dw-cc-posting-service-database@0.0.9 |
dw-cc-ps-spring-boot-starter@0.1.0-rc.1 |
same | tag at index 42, per-module regex (same-module predecessor exists) | |
| per-module regex, no same-module predecessor | TypeError |
…spring-boot-starter@0.1.0-rc.1 |
same, plus fallback warning |
test-positive.yml scenario |
0.2.0 |
0.2.0 |
0.2.0, plus fallback warning |
test-negative.yml scenario |
{"comments":[]} |
same | same |
- Baseline reproduces the reported failure —
setFailedmessage andTypeError— on every deep-index case - A tag at index 66 of 82 resolves a previous release after the fix
- With a per-module
include_regex, it resolves to a same-module tag - The genuine no-previous-release path exits with
setFailed's message and noTypeError - Default
include_regexof.*produces identical output before and after commit 2 - Both
test-positive.ymlandtest-negative.ymlscenarios reproduce unchanged, including the same-SHA guard -
test-positive.yml/test-negative.ymlactually executed — not done. They areworkflow_dispatch-only and need repo secrets plus write access to this repo, which I don't have. The last two rows above are the harness reproducing their scenarios, not real workflow runs.
I did not add a workflow test for the >30-releases path: covering it in test-positive.yml would mean creating 30+ throwaway releases in this repo on every run. Happy to add a Node-based regression test for the extracted script if you'd want that as a separate change.
Notes
- No breaking changes. No input or output signature changes, so
README.md/README.yamlare untouched.atmos.yamland the workflows are untouched. - No TypeScript rewrite, per the note at the top of
action.yml— the diff is deliberately minimal and this stays a0.x.xchange. - Unrelated observation, not fixed here: the
switch (context.eventName)block has the same missing-returnpattern. Theworkflow_dispatch-without-tagcase and thedefault:case both callcore.setFailedand then fall through intogetRelease({ release_id: context.payload.release.id }), throwing a second, more confusingTypeError. Left alone to keep this PR scoped — glad to follow up if you want it.
what
- Describe high-level what changed as a result of these commits (i.e. in plain-english, what do these changes mean?)
- Use bullet points to be concise and to the point.
why
- Provide the justifications for the changes (e.g. business case).
- Describe why these changes were made (e.g. why do these commits fix the problem?)
- Use bullet points to be concise and to the point.
references
- Link to any supporting github issues or helpful documentation to add some context (e.g. stackoverflow).
- Use
closes #123, if this PR closes a GitHub issue#123
🤖 Automatic Updates
Adde scheduled workflow @goruha (#9)
## what - Update workflows (`.github/workflows/`) to use `cloudposse-github-actions` org workflowswhy
- Part of migration GHA to
cloudposse-github-actionsorg
Replace Makefile with atmos.yaml @goruha (#8)
## what - Remove `Makefile` - Add `atmos.yaml`why
- Replace
build-harnesswithatmosfor readme genration
References
- DEV-3229 Migrate from build-harness to atmos