Repository navigation
Conversation
…on docs Placeholder packages now carry a README and a module that print a pointer to the Agent integration-management docs. The find job also selects names whose latest PyPI release is 0.0.1 so the old empty placeholders are republished at 0.0.2.
evalya-impact-summaryEvalya CI impact |
Codex Review Summary: ✅ CompletedThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
The bulk upgrade could exceed GitHub Actions’ 256-job matrix limit and block all publishing. There are 268 candidate packages, but their current PyPI eligibility remains unverified.
🤖 Bits Code Review · Commit fb11c1f
| packages.append(name) | ||
|
|
||
| with open(os.environ['GITHUB_OUTPUT'], 'a') as output: | ||
| output.write(f'packages={json.dumps(packages)}\n') |
There was a problem hiding this comment.
Batch the bulk upgrade before expanding the publishing matrix
Including existing 0.0.1 placeholders can push the bulk upgrade above GitHub Actions’ 256-job matrix limit. If at least 257 packages qualify, matrix expansion rejects the entire publishing job before any upload, leaving those packages eligible on subsequent runs. This checkout contains 268 distinct candidate packages; their current PyPI eligibility was not verified. Cap each nightly batch so the migration can make progress.
| output.write(f'packages={json.dumps(packages)}\n') | |
| output.write(f'packages={json.dumps(packages[:256])}\n') |
React: 👍 useful · 👎 wrong · 😕 unclear · 👀 right, but not acting on it
What each reaction means
- 👍 Useful: correct, and you're fixing it or will.
- 👎 Wrong: the finding is incorrect, for example the bug can't happen or the code was misread.
- 😕 Unclear: you can't tell what it means or why it matters.
- 👀 Right, not acting: correct, but intended, out of scope for this PR, a known risk, or too minor.
🤖 Bits Code Review · Open Bits AI session
There was a problem hiding this comment.
hehe @AAraKKe another usecase for the dispatcher? ;)
There was a problem hiding this comment.
Confirmed: a dry run of the finder against PyPI selects 265 packages, which would have exceeded the 256-job matrix limit on the first post-merge run. Applied the cap in 3254734 — published packages drop out of the selection, so the remainder is picked up on the next run. Also added workflow_dispatch so the batch can be pushed by hand.
There was a problem hiding this comment.
Actually I was proposing to do this through ddev not only to be able to have proper tests but because event bus would allow us to do this easily with better minting if I am not mistaken. This helps with the backfilling which we will need to do, the only problem is that all the old projects don't have trusted publishers enabled. So either we need to manually publish the package or enable trusted publishers everywhere.
The event bus proposal is to allow everything to be automted.
Validation ReportAll 22 validations passed. Show details
|
There was a problem hiding this comment.
More details
The PR publishes 0.0.2 placeholders with a README and import-time guidance directing users to Agent-managed installation. The new commit adds manual dispatch and caps publish batches at 256, addressing the matrix-limit problem documented in the PR discussion. Excess packages are deferred to subsequent runs. No reportable findings remain; the earlier review also had none. Wheel building and live publication remain unverified.
🤖 Bits Code Review · Commit 3254734
AAraKKe
left a comment
There was a problem hiding this comment.
I built the 0.0.2 wheel locally: twine check passes, the README lands in the metadata and importing the module prints the pointer. The 256 cap and workflow_dispatch also take care of the matrix limit Bits flagged.
What's left is making sure the backfill actually lands on every name we own.
Token-era projects need a one-off before this merges. 265 of our names are at an empty 0.0.1. 260 of them were uploaded before #24792 with the old API token (no provenance on PyPI, e.g. datadog-activemq). Only 5 went through this workflow with Trusted Publishing (e.g. datadog-kai-scheduler). PyPI doesn't expose publisher configuration publicly, so I can't confirm it, but unless someone registered this workflow on those 260 projects, their 0.0.2 upload gets rejected with invalid-publisher every night. Someone with owner access on PyPI has to do one of these:
- Register this workflow (
claim-pypi-name.yaml, environmentpublish-pypi) as a trusted publisher on each project. There's no API for it (pypi/warehouse#14456 is still open), only the publishing settings form. PyPI also caps registrations at 100 per user or IP per 24 hours (troubleshooting docs), so it takes at least 3 days or several admins. Scripting the form with an owner's logged-in session should work, but it isn't supported. - Run the publish command once, locally, with a short-lived user-scoped API token from a project owner. Much less work, but it departs from the direction of #24792 and leaves those projects without a trusted publisher if the placeholder ever needs to change again.
Whoever owns the PyPI org should pick one.
Retired integrations aren't covered. Four names we claimed belong to integrations that are no longer in the repo, so no pyproject.toml selects them and they stay empty. Details inline.
Publishing should move into a ddev command on the event bus, with the workflow just running it in a single job. It removes the matrix limit, keeps one failing package from blocking the rest and gives the retired names a place to live. Details inline.
My Feedback Legend
Here's a quick guide to the prefixes I use in my comments:
praise: no action needed, just celebrate!
note: just a comment/information, no need to take any action.
question: I need clarification or I'm seeking to understand your approach.
nit: A minor, non-blocking issue (e.g., style, typo). Feel free to ignore.
suggestion: I'm proposing an improvement. This is optional but recommended.
request: A change I believe is necessary before this can be merged.
The only blocking comments are request, any other type of comment can be applied at discretion of the developer.
| packages.append(name) | ||
|
|
||
| with open(os.environ['GITHUB_OUTPUT'], 'a') as output: | ||
| output.write(f'packages={json.dumps(packages[:256])}\n') |
There was a problem hiding this comment.
request: Following up on the dispatcher idea: not the Dispatcher itself, but the event bus it's built on fits well here. Let's move finding, building and publishing into a ddev command and have the workflow just run it in one job.
The cap fixes the first run, but packages that can't publish (see the token-era projects in the summary) stay at 0.0.1 and fill the first 256 slots every night. Anything sorting after them is never selected. Today that's datadog-weblogic through datadog-zscaler-private-access, and a new integration sorting there wouldn't get claimed either. A single job has no matrix limit, and the event bus isolates each package the same way ddev validate all isolates each validation. Something like:
@dataclass
class PlaceholderMessage(BaseMessage):
"""`id` is the PyPI project name."""
class PlaceholderProcessor(SyncProcessor[PlaceholderMessage]):
def process_message(self, message: PlaceholderMessage) -> None:
wheel = build_placeholder(message.id)
upload(wheel, token=mint_pypi_token())
class PlaceholderOrchestrator(EventBusOrchestrator):
async def on_initialize(self) -> None:
for name in names_to_publish(self._repo):
self.submit_message(PlaceholderMessage(id=name)) publish:
runs-on: ubuntu-latest
environment: publish-pypi
permissions:
contents: read
id-token: write
steps:
- uses: actions/checkout@...
- uses: ./.github/actions/setup-ddev
- run: ddev release placeholders publishsetup-ddev installs ddev from the checkout, so this doesn't wait on a ddev release.
Things the command takes over from gh-action-pypi-publish, based on Warehouse's mint_token:
- Mint a PyPI token per package from a fresh GitHub OIDC token. Each OIDC token can only be exchanged once, and each exchange turns at most one pending publisher into a project, so every new name needs its own exchange. Minted tokens also expire after 15 minutes.
- A single job can upload to all of our projects: the minted token is scoped to every project with a matching publisher.
- Keep running from
claim-pypi-name.yamlwithenvironment: publish-pypi. Matching uses the workflow file name and the environment, so uploading from anywhere else means registering a new publisher on every project. - Decide on attestations. The action signs them today (
datadog-kai-scheduler0.0.1has provenance). The command would need to sign withpypi-attestationsand pass them totwine upload --attestations, or deliberately stop publishing them for placeholders. - Accept a plain API token as well, so the one-off for the token-era projects runs the same code.
| import urllib.request | ||
|
|
||
| packages = [] | ||
| for pyproject in sorted(pathlib.Path('.').glob('*/pyproject.toml')): |
There was a problem hiding this comment.
request: This only sees integrations that are in the repo today. Four names we claimed belong to integrations removed since, and they're still empty 0.0.1 releases:
datadog-datadog-csi-driverdatadog-oracledatadog-snowflakedatadog-tokumx
I found them by checking every */pyproject.toml deleted since May 2024 (git log --diff-filter=D -- '*/pyproject.toml') against PyPI. PyPI has no API to list an organization's projects (pypi/warehouse#18041), so I'd keep a checked-in list of retired names that the command publishes along with the ones from pyproject.toml.
| MODULE_NAME=$(echo "${PACKAGE_NAME}" | tr - _) | ||
| mkdir -p dist "pkg_placeholder/${MODULE_NAME}" | ||
| cat > pkg_placeholder/README.md << EOF | ||
| This package is a placeholder. It exists only to reserve the name on PyPI and contains no functional code. |
There was a problem hiding this comment.
suggestion: PEP 541 lists "name squatting (package has no functionality or is empty)" as grounds for removing a project, and this sentence describes the package in pretty much those terms. Since PyPI is already looking at these, I'd describe it as what it is, a protection for users:
Datadog reserves this name to protect users from typosquatting and dependency confusion. It is not a Datadog integration.
Datadog integrations are installed and managed with the Datadog Agent, not with pip:
https://docs.datadoghq.com/agent/guide/integration-management/?tab=linux#install| [project] | ||
| name = "${PACKAGE_NAME}" | ||
| version = "0.0.1" | ||
| version = "0.0.2" |
There was a problem hiding this comment.
question: After this, the empty 0.0.1 stays in every project's history; pip just stops picking it. Do we know whether that's enough for PyPI, or do they expect it gone? As far as I know, deleting or yanking a release can only be done from the web UI, one project at a time, so that would be another one-off worth knowing about up front.
There was a problem hiding this comment.
We cannot delete a version. "Yanking" it just tells pip to skip it. Since the releases aren't actually malicious or broken, there's no need to yank them.
What does this PR do?
The
claim-pypi-nameworkflow now publishes placeholder packages with a README and a single module. Both point to the Agent integration-management docs: https://docs.datadoghq.com/agent/guide/integration-management/?tab=linux#install. The module prints the pointer on import. The packages were previously empty wheels.0.0.1— our old empty placeholders — beside names that don't exist yet. Merging this PR forces the update: the next scheduled run republishes every0.0.1placeholder as0.0.2with the README and module.0.0.2is required because PyPI rejects re-uploading the identical0.0.1wheel. The list is capped at 256 packages so the publish matrix stays within the GitHub Actions 256-job limit; the overflow is picked up on the next run. The workflow can also be triggered manually.README.md, adds it viareadmein the generated pyproject, and adds<module>/__init__.pyprinting the same message.only-includereplacesbypass-selectionso the module ships in the wheel.verify-metadatais removed (defaults to true) since the metadata is now valid.skip-existingstaystrue: it only tolerates rejections for distribution files that already exist on PyPI, so it never blocks the0.0.2upload — every selected package is at0.0.1or missing.Motivation
Users who
pip installan integration name get an empty wheel with no guidance. The placeholders now tell them integrations are installed and managed with the Agent, not pip.A third-party name whose latest release is also
0.0.1would be selected and fail to publish since we don't own it;fail-fastis off, so only that matrix leg fails.Checklist before requesting review