Skip to content

Commit 72b212d

Browse files
tensornullCometAPI
andauthored
Complete Public Preview pre-visibility validation (#9)
* ci: complete pre-visibility validation * docs: record pre-visibility validation evidence * ci: close workflow validation bypasses * ci: reject linked workflow definitions * ci: reject linked workflow directories * docs: finalize pre-visibility evidence --------- Co-authored-by: CometAPI <support@cometapi.com>
1 parent 70fc7fe commit 72b212d

11 files changed

Lines changed: 2311 additions & 317 deletions

File tree

.github/workflows/ci.yml

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,9 @@ jobs:
2525
timeout-minutes: 20
2626
steps:
2727
- name: Check out the candidate
28-
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
28+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
2929
- name: Set up Python
30-
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
30+
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
3131
with:
3232
python-version: "3.14"
3333
- name: Install the pinned uv frontend
@@ -65,9 +65,9 @@ jobs:
6565
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
6666
steps:
6767
- name: Check out the candidate
68-
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
68+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
6969
- name: Set up Python
70-
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
70+
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
7171
with:
7272
python-version: ${{ matrix.python-version }}
7373
- name: Install the pinned uv frontend
@@ -83,9 +83,9 @@ jobs:
8383
timeout-minutes: 20
8484
steps:
8585
- name: Check out the candidate
86-
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
86+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
8787
- name: Set up Python
88-
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
88+
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
8989
with:
9090
python-version: "3.10"
9191
- name: Install the pinned uv frontend
@@ -104,9 +104,9 @@ jobs:
104104
timeout-minutes: 20
105105
steps:
106106
- name: Check out the candidate
107-
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
107+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
108108
- name: Set up Python
109-
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
109+
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
110110
with:
111111
python-version: "3.14"
112112
- name: Install the pinned uv frontend
@@ -125,9 +125,9 @@ jobs:
125125
timeout-minutes: 25
126126
steps:
127127
- name: Check out the candidate
128-
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
128+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
129129
- name: Set up Python
130-
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
130+
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
131131
with:
132132
python-version: "3.14"
133133
- name: Install the pinned uv frontend
@@ -145,7 +145,7 @@ jobs:
145145
- name: Record immutable artifact digests
146146
run: sha256sum dist/* > artifact-sha256.txt
147147
- name: Retain verified artifacts
148-
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
148+
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
149149
with:
150150
name: python-distributions
151151
path: |
@@ -161,12 +161,20 @@ jobs:
161161
timeout-minutes: 35
162162
steps:
163163
- name: Check out the candidate
164-
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
164+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
165165
- name: Set up Python
166-
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
166+
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
167167
with:
168168
python-version: "3.14"
169169
- name: Install the pinned uv frontend
170170
run: python -m pip install --disable-pip-version-check "uv==$UV_VERSION"
171171
- name: Verify from a copied standalone repository
172172
run: python scripts/check_repository_independence.py
173+
- name: Download the verified package artifacts
174+
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
175+
with:
176+
name: python-distributions
177+
path: verified-artifacts
178+
- name: Recheck retained artifact digests
179+
working-directory: verified-artifacts
180+
run: sha256sum --check artifact-sha256.txt

.github/workflows/live-smoke.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,12 @@ jobs:
3333
environment: live-smoke
3434
steps:
3535
- name: Check out the trusted default branch
36-
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
36+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
3737
with:
3838
ref: ${{ github.event.repository.default_branch }}
3939
persist-credentials: false
4040
- name: Set up Python
41-
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
41+
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
4242
with:
4343
python-version: "3.14"
4444
- name: Install the pinned uv frontend

.github/workflows/publish.yml

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ jobs:
2727
contents: read
2828
steps:
2929
- name: Check out the published release tag
30-
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
30+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
3131
with:
3232
ref: refs/tags/${{ github.event.release.tag_name }}
3333
fetch-depth: 0
@@ -40,7 +40,7 @@ jobs:
4040
RELEASE_TAG: ${{ github.event.release.tag_name }}
4141
run: bash scripts/verify_release_trust.sh
4242
- name: Set up Python
43-
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
43+
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
4444
with:
4545
python-version: "3.14"
4646
- name: Install the pinned uv frontend
@@ -73,7 +73,7 @@ jobs:
7373
- name: Record immutable artifact digests
7474
run: sha256sum dist/* > artifact-sha256.txt
7575
- name: Retain only the verified release bundle
76-
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
76+
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
7777
with:
7878
name: release-${{ steps.version.outputs.version }}
7979
path: |
@@ -106,7 +106,7 @@ jobs:
106106
COMETAPI_LIVE_STOP_ON_FAILURE: "1"
107107
steps:
108108
- name: Check out the verified release commit
109-
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
109+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
110110
with:
111111
ref: ${{ needs.build.outputs.release-commit }}
112112
persist-credentials: false
@@ -115,7 +115,7 @@ jobs:
115115
RELEASE_COMMIT: ${{ needs.build.outputs.release-commit }}
116116
run: test "$(git rev-parse HEAD)" = "$RELEASE_COMMIT"
117117
- name: Set up Python
118-
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
118+
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
119119
with:
120120
python-version: "3.14"
121121
- name: Install the pinned uv frontend
@@ -142,7 +142,7 @@ jobs:
142142
id-token: write
143143
steps:
144144
- name: Download the verified release bundle
145-
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
145+
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
146146
with:
147147
name: release-${{ needs.build.outputs.version }}
148148
path: release-bundle
@@ -167,19 +167,19 @@ jobs:
167167
contents: read
168168
steps:
169169
- name: Check out the registry verification source
170-
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
170+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
171171
with:
172172
ref: ${{ needs.build.outputs.release-commit }}
173173
persist-credentials: false
174174
- name: Download the verified release bundle after checkout
175-
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
175+
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
176176
with:
177177
name: release-${{ needs.build.outputs.version }}
178178
path: release-bundle
179179
- name: Require retained pre-publication digest evidence
180180
run: test -f release-bundle/artifact-sha256.txt
181181
- name: Set up Python
182-
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
182+
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
183183
with:
184184
python-version: "3.14"
185185
- name: Install the pinned provenance verifier

ROADMAP.md

Lines changed: 113 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,119 @@ Pre-visibility dependency disposition:
114114

115115
| Item | Disposition | Evidence and required action |
116116
| --- | --- | --- |
117-
| Dependabot [PR #2](https://github.com/cometapi-dev/cometapi-python/pull/2): `actions/checkout` 4.2.2 to 7.0.1 | Deferred; must not merge as-is | Credential-free [CI run 29796719306](https://github.com/cometapi-dev/cometapi-python/actions/runs/29796719306) failed in every test lane because the mutable-action-reference regression test hard-codes the previous v4 checkout SHA and no longer exercises its replacement; dependent artifact and copied-checkout jobs were skipped. Revisit only with version-independent regression coverage and a completely successful replacement CI run. The failed run is not upgrade evidence. |
117+
| Dependabot [PR #1](https://github.com/cometapi-dev/cometapi-python/pull/1): `actions/download-artifact` 4.3.0 to 8.0.1 | Superseded by private [PR #9](https://github.com/cometapi-dev/cometapi-python/pull/9) | PR #9 applies the reviewed SHA pin throughout the release workflow and adds a credential-free CI artifact download plus SHA256 round trip. Its initial code-bearing [CI run 29907523251](https://github.com/cometapi-dev/cometapi-python/actions/runs/29907523251) passed. Close PR #1 after PR #9 merges; do not merge both. |
118+
| Dependabot [PR #2](https://github.com/cometapi-dev/cometapi-python/pull/2): `actions/checkout` 4.2.2 to 7.0.1 | Superseded by private [PR #9](https://github.com/cometapi-dev/cometapi-python/pull/9); must not merge as-is | 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 and passed initial code-bearing CI run 29907523251. Close PR #2 after PR #9 merges; the failed PR #2 run remains negative evidence only. |
119+
| 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. |
120+
| Dependabot [PR #4](https://github.com/cometapi-dev/cometapi-python/pull/4): `actions/upload-artifact` 4.6.2 to 7.0.1 | Superseded by 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, and passed initial code-bearing CI run 29907523251. Close PR #4 after PR #9 merges; do not merge both. |
121+
| 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. |
122+
| Dependabot [PR #6](https://github.com/cometapi-dev/cometapi-python/pull/6): `actions/setup-python` 5.6.0 to 7.0.0 | Superseded by 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 and passed initial code-bearing CI run 29907523251 on Python 3.10 through 3.14, the minimum OpenAI lane, package builds, and copied-checkout verification. Close PR #6 after PR #9 merges; do not merge both. |
123+
124+
Recorded pre-visibility replacement evidence on 2026-07-22:
125+
126+
Local and package evidence at commit
127+
`97a14ac6087db3c9205e66bcfbcc890dc23a7ca7`:
128+
129+
- `git diff --check`, `uv lock --check`, and `uv sync --locked` passed.
130+
- `uv run ruff check src tests scripts`,
131+
`uv run ruff format --check src tests scripts`, and `uv run pyright` passed.
132+
- `uv run pytest -m "not live"` passed with 173 tests passed and one separately
133+
marked live test deselected.
134+
- `uv run python scripts/check_version.py --expected 0.1.0a1 --require-changelog`
135+
and
136+
`uv run python scripts/check_version.py --require-public-preview-docs` passed.
137+
- `uv run python scripts/check_secrets.py` and
138+
`uv run python scripts/check_workflows.py` passed.
139+
- `rm -rf dist` completed, and `uv build` produced the `0.1.0a1` wheel and source
140+
distribution in the clean output directory.
141+
- `uv run twine check dist/*`,
142+
`uv run python scripts/check_artifacts.py dist/*`, and
143+
`uv run python scripts/check_clean_install.py dist/*` passed for both exact
144+
artifacts, including SHA256 digest generation.
145+
- `uv run python scripts/check_repository_independence.py` passed after copying
146+
the candidate into an empty temporary parent and rerunning its complete
147+
offline, workflow, build, artifact, and two-artifact clean-install gates.
148+
- `uv run python scripts/run_actionlint.py` and
149+
`uv run python scripts/run_actionlint.py --offline` passed with
150+
checksum-pinned actionlint 1.7.12.
151+
152+
Follow-up verifier-hardening evidence at commit
153+
`88560a889017e2bddc47c52bcaf51e97fa42bcd4`:
154+
155+
- `git diff --check`, `uv lock --check`, and `uv sync --locked` passed.
156+
- `uv run ruff check src tests scripts`,
157+
`uv run ruff format --check src tests scripts`, and `uv run pyright` passed.
158+
- `uv run pytest -m "not live"` passed with 197 tests passed and one separately
159+
marked live test deselected.
160+
- `uv run python scripts/check_version.py --expected 0.1.0a1 --require-changelog`,
161+
`uv run python scripts/check_version.py --require-public-preview-docs`,
162+
`uv run python scripts/check_secrets.py`, and
163+
`uv run python scripts/check_workflows.py` passed.
164+
- `uv run python scripts/run_actionlint.py` and
165+
`uv run python scripts/run_actionlint.py --offline` passed with
166+
checksum-pinned actionlint 1.7.12.
167+
- `uv build`, `uv run twine check dist/*`,
168+
`uv run python scripts/check_artifacts.py dist/*`, and
169+
`uv run python scripts/check_clean_install.py dist/*` passed for the rebuilt
170+
wheel and source distribution.
171+
- `uv run python scripts/check_repository_independence.py` passed the complete
172+
copied-checkout gate, including its offline suite, build, artifact checks, and
173+
independent clean installs of both artifacts.
174+
- Independent adversarial workflow review and targeted follow-up regression
175+
coverage found no remaining accepted hostile case after checking trigger
176+
filters, secret-context access, runner, container, matrix, working-directory,
177+
checkout, job, step, and environment overrides, arbitrary privileged actions,
178+
mutable refs, no-op and failure-swallowing commands, artifact ordering, and
179+
release-ref decoys.
180+
181+
Final workflow-inventory hardening evidence at commit
182+
`668b78f89e8962cc8ab1d1aca8fe3d24c38723ac`:
183+
184+
- `git diff --check`, `uv lock --check`, and `uv sync --locked` passed.
185+
- `uv run ruff check src tests scripts`,
186+
`uv run ruff format --check src tests scripts`, and `uv run pyright` passed.
187+
- `uv run pytest -m "not live"` passed with 200 tests passed and one separately
188+
marked live test deselected.
189+
- `uv run python scripts/check_version.py --expected 0.1.0a1 --require-changelog`,
190+
`uv run python scripts/check_version.py --require-public-preview-docs`,
191+
`uv run python scripts/check_secrets.py`, and
192+
`uv run python scripts/check_workflows.py` passed.
193+
- `uv run python scripts/run_actionlint.py` and
194+
`uv run python scripts/run_actionlint.py --offline` passed with
195+
checksum-pinned actionlint 1.7.12.
196+
- `uv run python scripts/check_repository_independence.py` passed the complete
197+
copied-checkout gate, including 200 offline tests, the package build, artifact
198+
inspection, and independent clean installs of the wheel and source
199+
distribution.
200+
201+
Failed or unavailable checks:
202+
203+
- None of the recorded final-candidate checks failed or were unavailable.
204+
Dependabot PR #2's failed run remains separate negative evidence for that PR,
205+
not replacement evidence for PR #9. An earlier intentional offline actionlint
206+
probe in a fresh detached worktree failed closed before the verified cache was
207+
populated; it is not final-candidate validation evidence.
208+
209+
Remote evidence:
210+
211+
- Private PR #9's credential-free initial code-bearing CI run 29907523251 passed
212+
quality, Python 3.10 through 3.14, minimum OpenAI, package, exact-artifact
213+
clean install, retained artifact digest, and copied-checkout jobs. The PR-only
214+
latest-within-major canary skipped as designed; scheduled or Dependabot
215+
execution remains unverified.
216+
- The canonical repository was confirmed private after the successful
217+
replacement run. No visibility, secret, environment, or
218+
repository-protection change was made.
219+
220+
Live evidence:
221+
222+
- The live-smoke path was not executed, and no live API request was made.
223+
Transport success and provider behavior therefore remain unverified.
224+
225+
Registry and release evidence:
226+
227+
- Release Please, immutable-release publishing, PyPI OIDC, provenance, and
228+
public-registry installation were not executed. No tag, release, or
229+
publication was created.
118230

119231
Changing the repository to public begins a short configuration interval; it
120232
does not establish Public Preview readiness by itself. The preview is ready

pyproject.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,11 @@ dev = [
3838
"pyright>=1.1.408,<2.0.0",
3939
"pytest>=8.3.5,<10.0.0",
4040
"pytest-asyncio>=0.24.0,<2.0.0",
41+
"pyyaml>=6.0.2,<7.0.0",
4142
"ruff>=0.12.0,<1.0.0",
4243
"tomli>=2.2.1,<3.0.0",
4344
"twine>=6.1.0,<7.0.0",
45+
"types-pyyaml>=6.0.12,<7.0.0",
4446
]
4547

4648
[build-system]

scripts/check_secrets.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,8 @@ def _scan_content(root: Path) -> list[str]:
9494
def _scan_workflow_scope(root: Path) -> list[str]:
9595
findings: list[str] = []
9696
workflow_root = root / ".github" / "workflows"
97+
if not workflow_root.is_dir():
98+
return findings
9799
ci = workflow_root / "ci.yml"
98100
if ci.is_file() and re.search(
99101
r"\$\{\{\s*secrets\.", ci.read_text(encoding="utf-8"), flags=re.IGNORECASE
@@ -110,7 +112,11 @@ def _scan_workflow_scope(root: Path) -> list[str]:
110112
findings.append(
111113
".github/workflows/publish.yml: exactly one job must receive id-token: write"
112114
)
113-
for path in workflow_root.glob("*.yml"):
115+
for path in sorted(
116+
candidate
117+
for candidate in workflow_root.iterdir()
118+
if candidate.is_file() and candidate.suffix in {".yaml", ".yml"}
119+
):
114120
text = path.read_text(encoding="utf-8")
115121
if path.name != "publish.yml" and "id-token: write" in text:
116122
findings.append(f"{path.relative_to(root)}: id-token: write is publish-job-only")

scripts/check_version.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,12 @@ def require_public_preview_docs() -> None:
160160
documents = _read_public_documents(violations)
161161
_check_project_identity(violations)
162162

163+
codeowners = Path(".github/CODEOWNERS")
164+
if codeowners.exists() or codeowners.is_symlink():
165+
violations.append(
166+
".github/CODEOWNERS: must remain absent until a real multi-maintainer model exists"
167+
)
168+
163169
try:
164170
license_text = Path("LICENSE").read_text(encoding="utf-8")
165171
except (OSError, UnicodeError) as exc:

0 commit comments

Comments
 (0)