Skip to content

fix ghapi 2.x, skip draft releases, add include_prereleases - #16

Merged
djpugh merged 8 commits into
mainfrom
fix/ghapi-2-and-draft-releases
Aug 5, 2026
Merged

fix ghapi 2.x, skip draft releases, add include_prereleases#16
djpugh merged 8 commits into
mainfrom
fix/ghapi-2-and-draft-releases

Conversation

@djpugh

@djpugh djpugh commented Aug 5, 2026

Copy link
Copy Markdown
Owner

Supersedes #14.

Problem

Three faults, each of which fails the whole docs build for any consumer.

ghapi 2.x. ghapi 2.0 made operation calls asynchronous by default, so paged(...) yields an async generator and iterating it raises 'async_generator' object is not iterable. ghapi is unpinned, so this arrives unannounced.

Draft releases. A draft is unpublished, so GitHub returns published_at: null, which ghapi surfaces as an empty AttrDict — not None, not a str. The existing isinstance(..., datetime) guard passed it straight into datetime.fromisoformat, raising argument must be str. Drafts also have an empty name, so they could only ever render as a broken, dateless entry.

Prereleases were not filterable at all.

Fix

_make_api() sets ghapi's sync flag when the installed signature declares it, selecting the synchronous transport. The flag does not exist on 1.x, which accepts arbitrary keyword arguments without necessarily ignoring them, so it is passed only when declared — both majors work while ghapi stays unpinned.

_coerce_published_at() branches on the value's type rather than the Python version. Drafts are skipped with a debug log; a published release somehow lacking a timestamp is warned about and skipped rather than failing the build. The version check remains only for choosing how to parse a string, since fromisoformat cannot handle a trailing Z before 3.11.

include_prereleases (default False) is added as a plugin config option and a per-block option. Drafts stay excluded regardless.

djpugh added 2 commits August 5, 2026 14:10
Two independent faults, both of which fail the whole docs build.

ghapi 2.0 made operation calls asynchronous by default, so `paged(...)` yields
an async generator and iterating it raises "'async_generator' object is not
iterable". The same release added a `sync` flag selecting a synchronous
transport; `_make_api` sets it when the installed signature declares it, so
ghapi 1.x is unaffected.

Draft releases were not filtered. A draft is unpublished, so GitHub returns no
`published_at` -- ghapi surfaces that as an empty AttrDict, which reached
`datetime.fromisoformat` and raised "argument must be str". Drafts also have an
empty name, so they could only ever render as a broken, dateless entry. They are
now skipped, and a published release somehow lacking a timestamp is warned about
and skipped rather than failing the build.

The date coercion now branches on the value's type rather than the Python
version. The version check remains only for choosing how to parse a string,
since fromisoformat cannot handle a trailing Z before 3.11.

Verified end to end: nskit's docs build previously failed on both faults and now
completes, rendering four releases with dates and excluding the draft.
Folds in #14's prerelease flag, with three corrections.

The filter reads `release.prerelease`. #14 spelt it `prelease`, and since a
missing key on ghapi's AttrDict is falsy, the flag would have silently never
filtered anything while appearing to work.

The option is wired through. #14 added it to PluginConfig and to
get_releases_as_markdown, but extension.py plucks each option individually and
was not updated, so the config never reached the call.

Naming is consistent: `include_prereleases` everywhere, rather than a plural
config key against a singular function parameter.

Drafts remain excluded regardless of the flag, since they are unpublished and
carry no timestamp.

The shared test fixtures now set `prerelease` explicitly, for the same reason
the existing comment gives for `draft`: a bare MagicMock returns a truthy mock
for any unset attribute, so an unset flag silently empties the result. One test
drives the filter through an AttrDict so a misspelt attribute raises instead of
quietly passing -- which is what let #14's typo through.
@github-actions github-actions Bot added the fix label Aug 5, 2026
paged() is an async generator on 2.x even with sync=True, so iterating it
raised "async_generator object is not iterable".
@djpugh

djpugh commented Aug 5, 2026

Copy link
Copy Markdown
Owner Author

Root cause of the docs-build failure was narrower than first thought: GhApi(sync=True) is not enough, because on ghapi 2.x paged() is an async generator regardless of the transport — hence 'async_generator' object is not iterable. 2.x exports sync_paged(); the module now binds whichever exists so the call site is version-agnostic.

Verified the full unit suite plus an unauthenticated end-to-end fetch against both ghapi 1.0.13 and 2.0.5.

djpugh added 5 commits August 5, 2026 17:58
Config._validate breaks on the first error, so only one is ever reported;
each option is now checked individually instead.
pipenv check delegates to safety, which prompts to install itself and
needs an API key, so it fails with EOFError on CI. Also add lxml, which
mypy needs for its cobertura report.
An unset MagicMock attribute is truthy, so every mocked release looked
like a draft prerelease and was filtered out of the built changelog.
Launching a browser held the temp file open, so cleanup failed on Windows
with WinError 32.
argcomplete 3.7.1 does not import on 3.8/3.9, so nox never started. pip
26.2 dropped an internal pip-tools relies on, so use uv to compile.
@sonarqubecloud

sonarqubecloud Bot commented Aug 5, 2026

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
C Security Rating on New Code (required ≥ A)

See analysis details on SonarQube Cloud

Catch issues before they fail your Quality Gate with our IDE extension SonarQube for IDE

@djpugh
djpugh merged commit 44ce60b into main Aug 5, 2026
43 of 58 checks passed
@djpugh
djpugh deleted the fix/ghapi-2-and-draft-releases branch August 5, 2026 17:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant