Skip to content

Commit 67bd189

Browse files
tensornullCometAPI
andauthored
fix: harden release metadata and runtime (#29)
* fix: harden release metadata and runtime * docs: record release please replacement --------- Co-authored-by: CometAPI <support@cometapi.com>
1 parent e07b4e7 commit 67bd189

13 files changed

Lines changed: 364 additions & 85 deletions

.github/workflows/publish.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ jobs:
4545
steps:
4646
- name: Open or update the release PR, or create its approved release
4747
id: release
48-
uses: googleapis/release-please-action@5c625bfb5d1ff62eadeeb3772007f7f66fdcf071 # v4.4.1
48+
uses: googleapis/release-please-action@45996ed1f6d02564a971a2fa1b5860e934307cf7 # v5.0.0, node24
4949
with:
5050
config-file: release-please-config.json
5151
manifest-file: .release-please-manifest.json

AGENTS.md

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,16 @@ Post-stable invariants:
152152
direct dependency's `result` to equal `success`. A skipped, cancelled,
153153
failed, or missing dependency must never make build, live smoke,
154154
publication, or registry verification eligible.
155+
9. Keep Release Please pinned to the reviewed `v5.0.0` commit
156+
`45996ed1f6d02564a971a2fa1b5860e934307cf7`, whose immutable action metadata
157+
uses `node24`. The workflow semantic checker must reject any different pin.
158+
10. `README.md` is the distribution long description and must remain accurate
159+
before and after publication. Use `python -m pip install cometapi`,
160+
unversioned project links, and publication-neutral maintenance language.
161+
Reject approval, unpublished, exact-version installation, and versioned
162+
release-link text in both source and built artifact metadata, and require
163+
each built long description to exactly match the source README. Post-release
164+
evidence changes must not rewrite README release state.
155165

156166
## Repository independence
157167

@@ -329,7 +339,9 @@ committed.
329339
evaluate skipped ancestry, reject cancellation and reruns, and require each
330340
direct dependency to succeed.
331341
- Keep README, roadmap, compatibility matrix, examples, and changelog aligned
332-
with shipped behavior. Use currently supported model IDs.
342+
with shipped behavior. README installation and availability guidance must be
343+
publication-neutral because it is embedded in immutable distribution
344+
metadata. Use currently supported model IDs.
333345
- All repository documentation is written in English.
334346

335347
The Public Preview readiness record requires

ARCHITECTURE.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,18 @@ local mocked/package evidence
128128
-> public registry digest, provenance, install, import, and mocked smoke
129129
```
130130

131+
The package metadata embeds `README.md` as its long description. Because wheel,
132+
sdist, and PyPI metadata are immutable, the README uses an unversioned install
133+
command and publication-neutral release language that remains accurate before
134+
and after a release. Source-document and artifact checks reject approval,
135+
unpublished, exact-version installation, and versioned release-link text; each
136+
artifact long description must also exactly match the source README.
137+
138+
Release Please is pinned to the immutable `v5.0.0` commit whose action metadata
139+
uses `node24`. The workflow semantic contract fixes that SHA and runtime
140+
disposition so GitHub does not need to force a deprecated Node 20 action onto a
141+
newer runtime.
142+
131143
This complete trust chain executed successfully in
132144
[release workflow run 30261746138](https://github.com/cometapi-dev/cometapi-python/actions/runs/30261746138)
133145
for release commit `31b68904141489ca04932edbf305ccf88af09372`, recovery tag

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,13 @@ automation.
66

77
## [Unreleased]
88

9+
### Fixed
10+
11+
- Pin Release Please to its reviewed Node 24 action and reject regressions to
12+
the deprecated Node 20 runtime.
13+
- Keep the README and built distribution long description publication-neutral,
14+
with an unpinned stable installation command and no transient approval state.
15+
916
### Documentation
1017

1118
- Record completed `0.1.1` publication, live, provenance, digest, and

README.md

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# CometAPI Python SDK
22

3-
> **Stable release:** `0.1.1` is available from PyPI.
3+
> **Stable release line:** `0.1.x` maintenance releases are available from PyPI.
44
55
`cometapi` is a thin Python adapter over the official OpenAI SDK for the
66
OpenAI-compatible CometAPI endpoint. It changes the default API key and base
@@ -29,16 +29,14 @@ Project links: [CometAPI](https://www.cometapi.com),
2929

3030
## Installation
3131

32-
Install the independently verified stable release from PyPI with:
32+
Install the latest stable release from PyPI with:
3333

3434
```bash
35-
python -m pip install 'cometapi==0.1.1'
35+
python -m pip install cometapi
3636
```
3737

38-
The immutable
39-
[GitHub release](https://github.com/cometapi-dev/cometapi-python/releases/tag/v0.1.1)
40-
and exact [PyPI release](https://pypi.org/project/cometapi/0.1.1/) record the
41-
published artifact.
38+
See the [CometAPI package on PyPI](https://pypi.org/project/cometapi/) for the
39+
current stable distribution and release history.
4240

4341
## Authentication and configuration
4442

RELEASING.md

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -167,11 +167,26 @@ secret separation, and checkout-before-bundle-download ordering. Its git-backed
167167
tests exercise accepted and rejected release histories locally; they still do
168168
not emulate GitHub Actions.
169169

170+
The Release Please step is pinned to
171+
`googleapis/release-please-action@45996ed1f6d02564a971a2fa1b5860e934307cf7`
172+
(`v5.0.0`), whose immutable action metadata selects `node24`. The semantic
173+
checker rejects any other pin so the workflow cannot silently regress to the
174+
deprecated Node 20 runtime.
175+
170176
Release mode (`check_version.py --require-releasable-docs`) also fails closed
171177
until project authorship, the canonical GitHub repository URL, the copyright
172-
holder, security and support contacts, and the approved README/changelog
173-
release state are present. Public Preview validation reports all discovered
174-
violations in one run and still returns non-zero when any violation exists.
178+
holder, security and support contacts, a publication-neutral README, and a dated
179+
changelog release section are present. Public Preview validation reports all
180+
discovered violations in one run and still returns non-zero when any violation
181+
exists.
182+
183+
`pyproject.toml` embeds `README.md` as the immutable distribution long
184+
description. The README therefore uses the unpinned
185+
`python -m pip install cometapi` command and unversioned project links. Release
186+
PRs and post-release evidence changes must not introduce approval, unpublished,
187+
or exact-version availability statements. Artifact inspection applies the same
188+
policy to wheel `METADATA` and sdist `PKG-INFO`, so source and registry-facing
189+
descriptions cannot drift.
175190

176191
## Workflow responsibilities
177192

@@ -326,7 +341,7 @@ feature or fix pull request
326341
-> required offline CI
327342
-> merge to the default branch
328343
-> automated release pull request
329-
-> human finalization of stable docs, metadata, and one-time bridge cleanup
344+
-> human review of generated versions, changelog, and durable metadata
330345
-> required release-PR CI, review, and merge
331346
-> immutable tag and GitHub release
332347
-> bounded API verification of immutable tag and commit identity
@@ -340,15 +355,15 @@ feature or fix pull request
340355
-> roadmap milestone marked released
341356
```
342357

343-
Stable `0.1.0` additionally requires the complete blocking Python matrix,
358+
Stable `0.1.0` additionally required the complete blocking Python matrix,
344359
executed README examples against the built package, trusted live evidence, and
345-
reviewed release-PR and changelog agreement. Before the stable release PR is
346-
merged, its finalization commit must state that `0.1.0` is approved for PyPI
347-
publication, use the stable installation command and classifier, and remove the
348-
one-time `last-release-sha` plus prerelease-versioning controls. The manifest,
360+
reviewed release-PR and changelog agreement. Its one-time finalization removed
361+
the `last-release-sha` and prerelease-versioning controls. Later maintenance
362+
releases keep those controls absent and must retain publication-neutral README
363+
metadata throughout the release and post-release sequence. The manifest,
349364
project metadata, lock file, and changelog must remain at the exact generated
350-
`0.1.0` version. If GitHub requires approval before checks run on the automated
351-
pull request, approve only that reviewed workflow execution and wait for every
365+
version. If GitHub requires approval before checks run on the automated pull
366+
request, approve only that reviewed workflow execution and wait for every
352367
blocking check.
353368

354369
## Immutable release publication recovery

ROADMAP.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ Recorded pre-visibility dependency dispositions:
143143
| Dependabot [PR #2](https://github.com/cometapi-dev/cometapi-python/pull/2): `actions/checkout` 4.2.2 to 7.0.1 | Closed unmerged; superseded by merged private [PR #9](https://github.com/cometapi-dev/cometapi-python/pull/9) | PR #2's [CI run 29796719306](https://github.com/cometapi-dev/cometapi-python/actions/runs/29796719306) failed because its regression test hard-coded the previous checkout SHA. PR #9 instead validates parsed action references independently of version, passed final CI run 29916685839, and squash-merged as `72b212dd72e66bbde9c6714329f72071cc1ca129`; PR #2 was closed without merging, and its failed run remains negative evidence only. |
144144
| Dependabot [PR #3](https://github.com/cometapi-dev/cometapi-python/pull/3): `pypa/gh-action-pypi-publish` 1.14.0 to 1.14.1 | Deferred; keep out of `main` | Pull-request CI does not execute the release-triggered OIDC publish action or prove PyPI publication, provenance, or registry installation. Revisit with an authorized release-path review and the separately required protected release evidence; credential-free CI success alone is insufficient. |
145145
| Dependabot [PR #4](https://github.com/cometapi-dev/cometapi-python/pull/4): `actions/upload-artifact` 4.6.2 to 7.0.1 | Closed unmerged; superseded by merged private [PR #9](https://github.com/cometapi-dev/cometapi-python/pull/9) | PR #9 applies the reviewed SHA pin in CI and release builds, requires missing artifacts to fail, retains digest evidence, passed final CI run 29916685839, and squash-merged as `72b212dd72e66bbde9c6714329f72071cc1ca129`; PR #4 was closed without merging. |
146-
| Dependabot [PR #5](https://github.com/cometapi-dev/cometapi-python/pull/5): `googleapis/release-please-action` 4.4.1 to 5.0.0 | Deferred; keep out of `main` | `RELEASE_PLEASE_ENABLED` remains disabled, and pull-request CI does not execute the gated write-capable Release Please action. Revisit only after its real config, manifest, permissions, and release behavior can be reviewed without treating a skipped action as execution evidence. |
146+
| Dependabot [PR #5](https://github.com/cometapi-dev/cometapi-python/pull/5): `googleapis/release-please-action` 4.4.1 to 5.0.0 | Superseded by authorized replacement [PR #29](https://github.com/cometapi-dev/cometapi-python/pull/29); keep PR #5 out of `main` and close it after PR #29 merges | PR #29 pins the reviewed upstream `v5.0.0` commit, verifies its Node 24 runtime through the workflow semantic contract, and carries the release-metadata hardening required for `0.1.2`. PR #5's failed CI is negative evidence from the former semantic check that required the old action pin; it must not be merged independently or treated as runtime evidence. |
147147
| Dependabot [PR #6](https://github.com/cometapi-dev/cometapi-python/pull/6): `actions/setup-python` 5.6.0 to 7.0.0 | Closed unmerged; superseded by merged private [PR #9](https://github.com/cometapi-dev/cometapi-python/pull/9) | PR #9 applies the reviewed SHA pin across CI, monitoring, and release workflows, passed final CI run 29916685839 on every blocking lane, and squash-merged as `72b212dd72e66bbde9c6714329f72071cc1ca129`; PR #6 was closed without merging. |
148148

149149
Recorded pre-visibility replacement evidence on 2026-07-22:
@@ -346,8 +346,9 @@ Public Preview remains ready only while:
346346
self-review.
347347
- Default-branch CI is rerun successfully after the public configuration.
348348
- The fail-closed content gate reports all violations together and then passes,
349-
repository self-containment and package gates pass, and the README accurately
350-
states prerelease and registry availability.
349+
repository self-containment and package gates pass, and the README uses
350+
publication-neutral stable installation guidance suitable for immutable
351+
distribution metadata.
351352
- An explicitly authorized protected live smoke passes within the four-request,
352353
16-output-token, 30-second-per-request, concurrency-one, stop-on-first-failure
353354
budget.

scripts/_checks.py

Lines changed: 45 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import sys
99
from collections.abc import Iterable
1010
from email.message import Message
11+
from email.parser import Parser
1112
from pathlib import Path
1213
from typing import cast
1314

@@ -31,6 +32,26 @@
3132
"Support": f"{CANONICAL_REPOSITORY}/blob/main/SUPPORT.md",
3233
"Security": CANONICAL_SECURITY,
3334
}
35+
PUBLIC_README_INSTALL_COMMAND = "python -m pip install cometapi"
36+
PUBLIC_README_FORBIDDEN_PATTERNS = (
37+
(r"(?i)\bpending[\s-]+owner(?:ship|s)?\b", "pending owner identity"),
38+
(r"(?i)\bapproved\s+for\s+pypi\s+publication\b", "publication approval state"),
39+
(r"(?i)\b(?:candidate|unreleased|unpublished)\b", "unpublished release state"),
40+
(r"(?i)\bno\s+pypi\s+publication\b", "missing PyPI publication"),
41+
(r"(?i)\b(?:has\s+)?not\s+been\s+published\b", "unpublished release state"),
42+
(
43+
r"(?i)\bdo\s+not\s+treat\b[^\n]*\bcurrently\s+available\b",
44+
"temporary availability warning",
45+
),
46+
(r"(?i)\blocal\s+candidate\b", "local-candidate narrative"),
47+
(r"(?i)\b0\.1\.\d+(?:a\d+)?\s+is\s+(?:available|approved)\b", "versioned release state"),
48+
(r"(?i)cometapi==\d+\.\d+\.\d+(?:a\d+)?", "version-pinned installation command"),
49+
(r"https://pypi\.org/project/cometapi/\d", "versioned PyPI release link"),
50+
(
51+
r"https://github\.com/cometapi-dev/cometapi-python/releases/tag/v\d",
52+
"versioned GitHub release link",
53+
),
54+
)
3455

3556

3657
class CheckError(RuntimeError):
@@ -96,25 +117,37 @@ def sha256_file(path: Path) -> str:
96117

97118
def parse_metadata(raw: bytes, source: str) -> Message:
98119
"""Parse package core metadata and require its identity fields."""
99-
message = Message()
100-
text = raw.decode("utf-8")
101-
for line in text.splitlines():
102-
if not line.strip():
103-
break
104-
if line[0].isspace():
105-
current = message.get_payload()
106-
message.set_payload(f"{current}\n{line}" if current else line)
107-
continue
108-
name, separator, value = line.partition(":")
109-
if separator:
110-
message[name] = value.strip()
120+
try:
121+
message = Parser().parsestr(raw.decode("utf-8"))
122+
except (UnicodeDecodeError, ValueError) as exc:
123+
raise CheckError(f"{source}: invalid metadata: {exc}") from exc
111124
if message.get("Name") != DIST_NAME:
112125
raise CheckError(f"{source}: expected Name: {DIST_NAME!r}")
113126
if message.get("Version") is None:
114127
raise CheckError(f"{source}: missing Version metadata")
115128
return message
116129

117130

131+
def metadata_description(message: Message, source: str) -> str:
132+
"""Return the rendered long description from a distribution metadata message."""
133+
payload = message.get_payload()
134+
if not isinstance(payload, str) or not payload.strip():
135+
raise CheckError(f"{source}: missing long description metadata")
136+
return payload
137+
138+
139+
def public_readme_release_violations(text: str) -> list[str]:
140+
"""Return transient or version-specific release statements in public README text."""
141+
return [
142+
label for pattern, label in PUBLIC_README_FORBIDDEN_PATTERNS if re.search(pattern, text)
143+
]
144+
145+
146+
def public_readme_has_install_command(text: str) -> bool:
147+
"""Return whether the public README contains the exact unpinned install command."""
148+
return any(line.strip() == PUBLIC_README_INSTALL_COMMAND for line in text.splitlines())
149+
150+
118151
def require_equal_versions(items: Iterable[tuple[str, str]]) -> str:
119152
"""Require all named version values to normalize to the same version."""
120153
normalized = [(name, normalize_version(value)) for name, value in items]

0 commit comments

Comments
 (0)