Repository navigation
fix(ci): run release jobs on hosted runners and gate the PyPI publish (ENG-2000) - #90
Open
lucas-koontz wants to merge 1 commit into
Open
lucas-koontz wants to merge 1 commit into
lucas-koontz wants to merge 1 commit into
Conversation
The release integration tests and the PyPI publish run on ubuntu-latest instead of mdb-dev. GitHub recommends GitHub-hosted runners for public repositories. Both jobs keep the secrets and variables they use today. deploy_to_pypi publishes only when the triggering run of "Run Integration Tests on Release" succeeded, came from a release event and has its head in this repository. It checks out that run's head_sha, so PyPI gets the commit the tests ran on, not the default branch's head. The release test step passes MINDS_API_TOKEN and MINDS_API_BASE_URL, the names tests/integration/config.py reads. With MINDS_API_KEY and BASE_URL, config.py fails at import and pytest exits 4 before any test runs, so the new gate could never pass. Both release workflows pin actions/checkout and actions/setup-python to commit SHAs with version comments, and their checkouts set persist-credentials: false. The clean step globs ./*.egg-info, and the publish step's comment says what it builds. The workflow_run trigger carries an inline zizmor ignore for dangerous-triggers. tests/unit/test_workflows.py checks the publish conditions, the checkout ref and that workflow_run stays the only trigger. It also checks that no workflow names a self-hosted runner label, that a failing test fails the release run, the env names, the pins and persist-credentials. requirements_test.txt gains pyyaml for it. Part of Lucas Koontz's ticket "Take the public repos off the credentialed runner group and require devops to release a privileged run". Refs: ENG-2000
|
I have read the CLA Document and I hereby sign the CLA You can retrigger this bot by commenting recheck in this Pull Request. Posted by the CLA Assistant Lite bot. |
This was referenced Oct 4, 2026
Draft
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
User story
As a maintainer who publishes minds-sdk releases
I want the release tests and the PyPI upload to run on GitHub-hosted runners, with the upload waiting for the tests to pass
So that PyPI gets only versions whose integration tests passed, built from the commit those tests ran on
Why this matters
When a maintainer publishes a minds-sdk release, PyPI gets it whether or not its release tests pass, so users can install a version that failed them. Versions 2.0.0 and 2.0.1 both reached PyPI after their release test runs failed. The 2.0.1 run could not have passed: the workflow sets
MINDS_API_KEYandBASE_URL, buttests/integration/config.pyreadsMINDS_API_TOKENandMINDS_API_BASE_URL, so pytest exits 4 at import. Both jobs also run on the self-hostedmdb-devrunner, and GitHub recommends GitHub-hosted runners for public repositories.What happens today
flowchart TD R["Maintainer publishes a release"] --> T["test_on_deploy.yml runs on mdb-dev"] T --> X["config.py fails at import, pytest exits 4"] X --> W["deploy.yml runs without checking the result"] W --> P["deploy_to_pypi on mdb-dev uploads the default branch head"]What should happen
flowchart TD R["Maintainer publishes a release"] --> T["test_on_deploy.yml runs on ubuntu-latest"] T --> X["Integration tests run against mdb.ai"] X --> W{"Succeeded, from a release, head in this repository?"} W -- yes --> P["deploy_to_pypi on ubuntu-latest uploads the tested commit"] W -- no --> N["Publish skipped"]Acceptance criteria
testjob intest_on_deploy.ymlanddeploy_to_pypiindeploy.ymlrun onubuntu-latest. No workflow in this repository namesmdb-dev,mdb-prodorself-hosted, in any letter case.deploy_to_pypiruns only when the triggering run concludedsuccess, came from areleaseevent and has its head inmindsdb/minds_python_sdk, anddeploy.ymlhas no trigger besidesworkflow_run. Negative: a failed release test run, or a run of a same-named workflow on another event or from another repository, skips the publish.deploy_to_pypichecks outgithub.event.workflow_run.head_sha, the commit the release tests ran on.MINDS_API_TOKENandMINDS_API_BASE_URL, and with thempytest tests/integration --collect-onlycollects 5 tests. Negative: neither the test job nor its pytest step setscontinue-on-error, and the pytest command has no||, so a failing test fails the release run.# vX.Y.Zcomment, and both checkouts setpersist-credentials: false.tests/unit/test_workflows.pyfails when any criterion above regresses, except the collect-only count, and the PR workflow runs it on Python 3.8 to 3.11.How to test
Start in a clean checkout of this branch, with Python 3.8 or newer, no
.envfile and noMINDS_*variables in your shell.pip install -r requirements.txt -r requirements_test.txt, thenPYTHONPATH=./ pytest tests/unit -q. Expect24 passed, 14 of them intests/unit/test_workflows.py.PYTHONPATH=./ MINDS_API_TOKEN=dummy MINDS_API_BASE_URL=https://mdb.ai pytest tests/integration --collect-only -q. Expect5 tests collected. Repeat withMINDS_API_KEY=dummy BASE_URL=https://mdb.aiinstead, and expect exit code 4 withAttributeError: 'NoneType' object has no attribute 'strip'attests/integration/config.py:22.actionlintat the repository root, and expect no output. Runzizmor --offline --persona=regular .github/workflows/deploy.yml .github/workflows/test_on_deploy.yml, and expect one informational finding,use-trusted-publishing.git checkout origin/main -- .github/workflows/deploy.yml .github/workflows/test_on_deploy.yml, thenPYTHONPATH=./ pytest tests/unit/test_workflows.py -q. Expect9 failed, 5 passed. Restore the files withgit checkout HEAD -- .github/workflows/.Run Integration Tests on Releasejob on a GitHub-hosted runner, withtest_sdk_happy_path_lifecyclepassed, not skipped. ExpectPublish to PyPIto run only after that run succeeds, and PyPI to get the version inminds/__about__.pyat the release's commit. This first release is the step most likely to find a problem.Notes for the reviewer
This PR depends on no other PR and can merge now. It sits in step 4 of the merge order under
Ships with, beside mindsdb/engine, mindsdb/data-vault and mindsdb/hashnode-starter-kit. It has to be onmainbefore the final step in that list.It needs no operator steps. Both jobs read the secrets and the variable they read today:
MINDS_API_KEY,PYPI_PASSWORDandCI_PYTHON_VERSION. No environment, secret or variable changes.Rollback: revert this commit on
main. The release jobs then requestmdb-devagain, and the publish goes back to uploading the default branch's head whatever the test result.Watch the first release after this merges. The next release reaches PyPI only if the integration suite passes against
https://mdb.ai. No release run of the suite has passed yet. I could not run it end to end here, because it needsMINDS_API_KEYand creates and drops datasources and minds on mdb.ai.A re-run of a failed release run tests the same commit. Re-runs reuse the original event's commit. So a re-run helps only when the cause was outside the code: a flaky answer from the mind, a rotated
MINDS_API_KEYor a fix on mdb.ai. Re-run the test run, not the skipped publish run. The publish re-run reuses its original event and skips again, while a passing test re-run starts a new publish run. A fix to the tests or the package needs a release at the fixed commit. Delete the failed release and its tag and publish it again at that commit, or bump the version and cut a new release. The failed version never reached PyPI, so publishing it again is safe.A skipped end-to-end test does not block the publish.
test_sdk_happy_path_lifecycleis the only test that asks a mind a question. Itsdb_ground_truthfixture connects tosamples.mindsdb.com:5432and skips the test on any connection error, and pytest still exits 0. This change moves that connection onto GitHub-hosted runners, so check that the first release's log shows the test passed.The guard also checks that the triggering run came from a release.
workflow_runmatches the triggering workflow by name only. Without the event check, a successful run of a same-named workflow on another event, such as a push, would publish its branch. Every condition in theif:reads a field GitHub sets on the triggering run.workflow_runstays, behind the guard and an inline zizmor ignore. zizmor reportsdangerous-triggerson everyworkflow_runtrigger and anchors the finding on theon:key, so the ignore covers every trigger in the file.tests/unit/test_workflows.pymakes up for that: it pins theif:, and it fails whendeploy.ymlgains a second trigger. A publish job intest_on_deploy.ymlbehindneeds: testwould removeworkflow_runaltogether. That move fits best with PyPI trusted publishing, which binds the publisher to one workflow file.The pins change nothing that runs today.
actions/checkoutv4andv4.4.0both point at11d5960a, andactions/setup-pythonv5.6.0points ata26af69b.Deliberate omissions.
PYPI_PASSWORDsecret. PyPI trusted publishing needs a publisher registered on PyPI first, so it is a separate change, and zizmor's informationaluse-trusted-publishingfinding stays.test_on_pr.ymlandcodeql.ymlkeep their action references. The pin test covers only the two release workflows, which hold the publishing and API secrets.||check is a heuristic.; exit 0,set +eor a wrapper script would still swallow a pytest failure.mdb-dev,mdb-prodorself-hosted, and the pin test fails on a comment containinguses:in either release workflow.tests/unit/test_workflows.pyreads the workflow YAML as plain dicts. Each test reads two or three keys, so a typed model of GitHub's workflow schema would add more code than the checks it serves.pytest.ini'shappy_pathmarker line still producesUnknown config option: happy_path, as it does onmain.Verified locally
pytest tests/unit -qin local venvs withrequirements.txtandrequirements_test.txtinstalled, on Python 3.8.20, 3.9.6, 3.10.13 and 3.11.1324 passedon each. Each run warnsUnknown config option: happy_path, asmaindoes. 3.9.6 is macOS's system Python and adds urllib3's LibreSSL warningpytest tests/integration --collect-only -qwith dummyMINDS_API_TOKENandMINDS_API_BASE_URL5 tests collected, exit 0MINDS_API_KEYandBASE_URL, the namesmainpassesAttributeErrorattests/integration/config.py:22, exit 4tests/unit/test_workflows.pymain's two release workflows againsttests/unit/test_workflows.pyactionlint1.7.12main: unknown runner labelmdb-devin both release workflows, and shellcheck SC2035 on the clean stepzizmor1.28.0--offline --persona=regularon both release workflowsuse-trusted-publishing. Onmain: 15 findings, includingdangerous-triggers, 4unpinned-usesand 2artipackedruff check0.15.20 ontests/unit/test_workflows.pygh apion the pinned action tags, 2026-10-03actions/checkoutv4andv4.4.0both point at11d5960a.actions/setup-pythonv5andv5.6.0both point ata26af69bgh apion the v2.0.0 and v2.0.1 release runs, and the PyPI JSON APIreleaseand conclusionfailure. Both publish runs concludedsuccess, and PyPI shows each version uploaded after its failed test runorigin/mainNot run: the integration suite end to end, which needs
MINDS_API_KEYand writes to mdb.ai, andpython setup.py sdistwithtwine upload, which would publish.Ships with
Merge order
argocd-pr-env-deployto the github-actions merge commit from step 2. The operator also creates the deployer's GitHub App and sets its client ID and private key in thestagingandprodenvironments of cowork and cowork-server.staging. This PR, mindsdb/engine#7, mindsdb/data-vault#4 and mindsdb/hashnode-starter-kit#39 merge intomain. These four depend on no other step.stagingtag, mindsdb/scratchpad-controller#80 and mindsdb/argocd-envs#25 merge.stagingtomainin their next release.mainbuilds push through the prod writer roles.mainandstaging.Sibling PRs, in merge order:
build-push-ecrgainsbuilder: localfor GitHub-hosted builds, andargocd-pr-env-deploytakes the pull request as inputs and checks its image in the dev tier.Refs: ENG-2000