Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 21 additions & 13 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ jobs:
timeout-minutes: 20
steps:
- name: Check out the candidate
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Set up Python
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: "3.14"
- name: Install the pinned uv frontend
Expand Down Expand Up @@ -65,9 +65,9 @@ jobs:
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
steps:
- name: Check out the candidate
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Set up Python
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: ${{ matrix.python-version }}
- name: Install the pinned uv frontend
Expand All @@ -83,9 +83,9 @@ jobs:
timeout-minutes: 20
steps:
- name: Check out the candidate
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Set up Python
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: "3.10"
- name: Install the pinned uv frontend
Expand All @@ -104,9 +104,9 @@ jobs:
timeout-minutes: 20
steps:
- name: Check out the candidate
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Set up Python
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: "3.14"
- name: Install the pinned uv frontend
Expand All @@ -125,9 +125,9 @@ jobs:
timeout-minutes: 25
steps:
- name: Check out the candidate
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Set up Python
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: "3.14"
- name: Install the pinned uv frontend
Expand All @@ -145,7 +145,7 @@ jobs:
- name: Record immutable artifact digests
run: sha256sum dist/* > artifact-sha256.txt
- name: Retain verified artifacts
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: python-distributions
path: |
Expand All @@ -161,12 +161,20 @@ jobs:
timeout-minutes: 35
steps:
- name: Check out the candidate
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Set up Python
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: "3.14"
- name: Install the pinned uv frontend
run: python -m pip install --disable-pip-version-check "uv==$UV_VERSION"
- name: Verify from a copied standalone repository
run: python scripts/check_repository_independence.py
- name: Download the verified package artifacts
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: python-distributions
path: verified-artifacts
- name: Recheck retained artifact digests
working-directory: verified-artifacts
run: sha256sum --check artifact-sha256.txt
4 changes: 2 additions & 2 deletions .github/workflows/live-smoke.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,12 @@ jobs:
environment: live-smoke
steps:
- name: Check out the trusted default branch
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ github.event.repository.default_branch }}
persist-credentials: false
- name: Set up Python
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: "3.14"
- name: Install the pinned uv frontend
Expand Down
18 changes: 9 additions & 9 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ jobs:
contents: read
steps:
- name: Check out the published release tag
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: refs/tags/${{ github.event.release.tag_name }}
fetch-depth: 0
Expand All @@ -40,7 +40,7 @@ jobs:
RELEASE_TAG: ${{ github.event.release.tag_name }}
run: bash scripts/verify_release_trust.sh
- name: Set up Python
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: "3.14"
- name: Install the pinned uv frontend
Expand Down Expand Up @@ -73,7 +73,7 @@ jobs:
- name: Record immutable artifact digests
run: sha256sum dist/* > artifact-sha256.txt
- name: Retain only the verified release bundle
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: release-${{ steps.version.outputs.version }}
path: |
Expand Down Expand Up @@ -106,7 +106,7 @@ jobs:
COMETAPI_LIVE_STOP_ON_FAILURE: "1"
steps:
- name: Check out the verified release commit
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ needs.build.outputs.release-commit }}
persist-credentials: false
Expand All @@ -115,7 +115,7 @@ jobs:
RELEASE_COMMIT: ${{ needs.build.outputs.release-commit }}
run: test "$(git rev-parse HEAD)" = "$RELEASE_COMMIT"
- name: Set up Python
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: "3.14"
- name: Install the pinned uv frontend
Expand All @@ -142,7 +142,7 @@ jobs:
id-token: write
steps:
- name: Download the verified release bundle
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: release-${{ needs.build.outputs.version }}
path: release-bundle
Expand All @@ -167,19 +167,19 @@ jobs:
contents: read
steps:
- name: Check out the registry verification source
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ needs.build.outputs.release-commit }}
persist-credentials: false
- name: Download the verified release bundle after checkout
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: release-${{ needs.build.outputs.version }}
path: release-bundle
- name: Require retained pre-publication digest evidence
run: test -f release-bundle/artifact-sha256.txt
- name: Set up Python
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: "3.14"
- name: Install the pinned provenance verifier
Expand Down
114 changes: 113 additions & 1 deletion ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,119 @@ Pre-visibility dependency disposition:

| Item | Disposition | Evidence and required action |
| --- | --- | --- |
| 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. |
| 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. |
| 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. |
| 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. |
| 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. |
| 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. |
| 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. |

Recorded pre-visibility replacement evidence on 2026-07-22:

Local and package evidence at commit
`97a14ac6087db3c9205e66bcfbcc890dc23a7ca7`:

- `git diff --check`, `uv lock --check`, and `uv sync --locked` passed.
- `uv run ruff check src tests scripts`,
`uv run ruff format --check src tests scripts`, and `uv run pyright` passed.
- `uv run pytest -m "not live"` passed with 173 tests passed and one separately
marked live test deselected.
- `uv run python scripts/check_version.py --expected 0.1.0a1 --require-changelog`
and
`uv run python scripts/check_version.py --require-public-preview-docs` passed.
- `uv run python scripts/check_secrets.py` and
`uv run python scripts/check_workflows.py` passed.
- `rm -rf dist` completed, and `uv build` produced the `0.1.0a1` wheel and source
distribution in the clean output directory.
- `uv run twine check dist/*`,
`uv run python scripts/check_artifacts.py dist/*`, and
`uv run python scripts/check_clean_install.py dist/*` passed for both exact
artifacts, including SHA256 digest generation.
- `uv run python scripts/check_repository_independence.py` passed after copying
the candidate into an empty temporary parent and rerunning its complete
offline, workflow, build, artifact, and two-artifact clean-install gates.
- `uv run python scripts/run_actionlint.py` and
`uv run python scripts/run_actionlint.py --offline` passed with
checksum-pinned actionlint 1.7.12.

Follow-up verifier-hardening evidence at commit
`88560a889017e2bddc47c52bcaf51e97fa42bcd4`:

- `git diff --check`, `uv lock --check`, and `uv sync --locked` passed.
- `uv run ruff check src tests scripts`,
`uv run ruff format --check src tests scripts`, and `uv run pyright` passed.
- `uv run pytest -m "not live"` passed with 197 tests passed and one separately
marked live test deselected.
- `uv run python scripts/check_version.py --expected 0.1.0a1 --require-changelog`,
`uv run python scripts/check_version.py --require-public-preview-docs`,
`uv run python scripts/check_secrets.py`, and
`uv run python scripts/check_workflows.py` passed.
- `uv run python scripts/run_actionlint.py` and
`uv run python scripts/run_actionlint.py --offline` passed with
checksum-pinned actionlint 1.7.12.
- `uv build`, `uv run twine check dist/*`,
`uv run python scripts/check_artifacts.py dist/*`, and
`uv run python scripts/check_clean_install.py dist/*` passed for the rebuilt
wheel and source distribution.
- `uv run python scripts/check_repository_independence.py` passed the complete
copied-checkout gate, including its offline suite, build, artifact checks, and
independent clean installs of both artifacts.
- Independent adversarial workflow review and targeted follow-up regression
coverage found no remaining accepted hostile case after checking trigger
filters, secret-context access, runner, container, matrix, working-directory,
checkout, job, step, and environment overrides, arbitrary privileged actions,
mutable refs, no-op and failure-swallowing commands, artifact ordering, and
release-ref decoys.

Final workflow-inventory hardening evidence at commit
`668b78f89e8962cc8ab1d1aca8fe3d24c38723ac`:

- `git diff --check`, `uv lock --check`, and `uv sync --locked` passed.
- `uv run ruff check src tests scripts`,
`uv run ruff format --check src tests scripts`, and `uv run pyright` passed.
- `uv run pytest -m "not live"` passed with 200 tests passed and one separately
marked live test deselected.
- `uv run python scripts/check_version.py --expected 0.1.0a1 --require-changelog`,
`uv run python scripts/check_version.py --require-public-preview-docs`,
`uv run python scripts/check_secrets.py`, and
`uv run python scripts/check_workflows.py` passed.
- `uv run python scripts/run_actionlint.py` and
`uv run python scripts/run_actionlint.py --offline` passed with
checksum-pinned actionlint 1.7.12.
- `uv run python scripts/check_repository_independence.py` passed the complete
copied-checkout gate, including 200 offline tests, the package build, artifact
inspection, and independent clean installs of the wheel and source
distribution.

Failed or unavailable checks:

- None of the recorded final-candidate checks failed or were unavailable.
Dependabot PR #2's failed run remains separate negative evidence for that PR,
not replacement evidence for PR #9. An earlier intentional offline actionlint
probe in a fresh detached worktree failed closed before the verified cache was
populated; it is not final-candidate validation evidence.

Remote evidence:

- Private PR #9's credential-free initial code-bearing CI run 29907523251 passed
quality, Python 3.10 through 3.14, minimum OpenAI, package, exact-artifact
clean install, retained artifact digest, and copied-checkout jobs. The PR-only
latest-within-major canary skipped as designed; scheduled or Dependabot
execution remains unverified.
- The canonical repository was confirmed private after the successful
replacement run. No visibility, secret, environment, or
repository-protection change was made.

Live evidence:

- The live-smoke path was not executed, and no live API request was made.
Transport success and provider behavior therefore remain unverified.

Registry and release evidence:

- Release Please, immutable-release publishing, PyPI OIDC, provenance, and
public-registry installation were not executed. No tag, release, or
publication was created.

Changing the repository to public begins a short configuration interval; it
does not establish Public Preview readiness by itself. The preview is ready
Expand Down
2 changes: 2 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,11 @@ dev = [
"pyright>=1.1.408,<2.0.0",
"pytest>=8.3.5,<10.0.0",
"pytest-asyncio>=0.24.0,<2.0.0",
"pyyaml>=6.0.2,<7.0.0",
"ruff>=0.12.0,<1.0.0",
"tomli>=2.2.1,<3.0.0",
"twine>=6.1.0,<7.0.0",
"types-pyyaml>=6.0.12,<7.0.0",
]

[build-system]
Expand Down
8 changes: 7 additions & 1 deletion scripts/check_secrets.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,8 @@ def _scan_content(root: Path) -> list[str]:
def _scan_workflow_scope(root: Path) -> list[str]:
findings: list[str] = []
workflow_root = root / ".github" / "workflows"
if not workflow_root.is_dir():
return findings
ci = workflow_root / "ci.yml"
if ci.is_file() and re.search(
r"\$\{\{\s*secrets\.", ci.read_text(encoding="utf-8"), flags=re.IGNORECASE
Expand All @@ -110,7 +112,11 @@ def _scan_workflow_scope(root: Path) -> list[str]:
findings.append(
".github/workflows/publish.yml: exactly one job must receive id-token: write"
)
for path in workflow_root.glob("*.yml"):
for path in sorted(
candidate
for candidate in workflow_root.iterdir()
if candidate.is_file() and candidate.suffix in {".yaml", ".yml"}
):
text = path.read_text(encoding="utf-8")
if path.name != "publish.yml" and "id-token: write" in text:
findings.append(f"{path.relative_to(root)}: id-token: write is publish-job-only")
Expand Down
6 changes: 6 additions & 0 deletions scripts/check_version.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,12 @@ def require_public_preview_docs() -> None:
documents = _read_public_documents(violations)
_check_project_identity(violations)

codeowners = Path(".github/CODEOWNERS")
if codeowners.exists() or codeowners.is_symlink():
violations.append(
".github/CODEOWNERS: must remain absent until a real multi-maintainer model exists"
)

try:
license_text = Path("LICENSE").read_text(encoding="utf-8")
except (OSError, UnicodeError) as exc:
Expand Down
Loading