Repository navigation
feat(actions): add a hosted build mode, PR-env inputs and commit pins (ENG-2000) - #71
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
build-push-ecr gains a `builder` input. The default, `remote`, runs the same create-repository, set-repository-policy, buildx create and build calls as before. `local` builds on the docker-container builder that setup-buildx-action starts on the runner. It caches layers in the GitHub Actions cache under module-name, and it never creates an ECR repository or changes a repository policy. So a caller job can push with a role it assumes through GitHub OIDC, and that role needs push rights only. In local mode, crazy-max/ghaction-github-runtime first exports the cache service's ACTIONS_* variables, which buildx needs and run steps do not get. The BuildKit container runs moby/buildkit v0.33.1, pinned to its multi-arch index digest, because it holds the ECR registry credentials while it pushes. argocd-pr-env-deploy gains `repository`, `pr-number` and `head-sha` inputs. They default to the pull_request event's values, so today's callers pass nothing new. A scheduled or dispatched workflow passes them to deploy a named PR. Before any API, ECR or ArgoCD call, the action refuses a value that is not owner/name, a number or a 40-character SHA, and it never prints the refused value. Outside a pull_request run it deploys the PR head's development-head-<sha> tag, because that run built no image of its own. The action's ECR existence check now uses one repository per repo. When mindsdb-<repo>-dev exists, it checks PR image tags there and only there. Otherwise it checks mindsdb-<repo>. It asks describe-images whether the -dev repository exists, so ecr:DescribeImages stays the only ECR permission the action needs. Every third-party action in build-push-ecr, snyk-docker-scan and setup-env is pinned to the commit its tag points at today, with the version in a comment. snyk-docker-scan also pins the Snyk CLI to v1.1307.4, the current release. stale-deploy-label's prune job runs on ubuntu-latest instead of mdb-dev. The README documents the local builder, the new inputs, the one-repository check and how to bump the BuildKit pin. Its secret-source table describes what an environment's deployment branch policy can do. New pytest suites run the build and deploy step scripts under bash with aws, docker, curl and argocd stubbed, and assert on the argv of each aws, docker and argocd call. Another suite fails if a pin loses its commit, digest or version. 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
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 of a public repository that builds and deploys with these actions
I want
build-push-ecrto build on a GitHub-hosted runner, andargocd-pr-env-deployto deploy a PR that another workflow namesSo that my repository's builds run on GitHub-hosted runners and its PR environments keep deploying
Why this matters
Today a maintainer who builds a public repository's image with
build-push-ecrhas to run that job onmdb-dev, because the action builds on the in-cluster BuildKit service with the runner pod's own AWS role. GitHub recommends GitHub-hosted runners for public repositories, because anyone can open a pull request there. Withbuilder: local, the same action builds on a GitHub-hosted runner with a role the job assumes through GitHub OIDC, and private callers keep today's commands.argocd-pr-env-deployused to work only inside apull_requestrun, so it now takes the PR as inputs, and a scheduled workflow in another repository can deploy PR environments.Acceptance criteria
builderruns the sameawsanddockercommands as before, argument for argument, and gets the sameimageoutput.builder: local, the action makes noawscall and creates no remote builder. Its build adds only--cache-from type=gha,scope=<module-name> --cache-to type=gha,scope=<module-name>,mode=max, on a BuildKit container started frommoby/buildkit:v0.33.1by its index digest.buildervalue, the empty string included, fails the build step before anyawsordockercall.build-push-ecr,snyk-docker-scan,setup-env,helm-deployandargocd-pr-env-deployis pinned to a full commit SHA with a# vX.Y.Zcomment, andsnyk-docker-scaninstalls Snyk CLIv1.1307.4.pull_requestrun ofargocd-pr-env-deploythat passes none of the new inputs deploysdevelopment-<merge SHA>, as before. Ascheduleorworkflow_dispatchrun that passesrepository,pr-numberandhead-shadeploysdevelopment-head-<head-sha>for that PR.mindsdb-<repo>-devexists, the action checks that repo's PR image tags there and only there. Otherwise it checksmindsdb-<repo>. Negative: a tag that exists only in the other repository fails the step.repositorythat is not owner/name, apr-numberthat is not digits, or ahead-shathat is not 40 lowercase hex characters stops the step before any GitHub API, ECR or ArgoCD call. The log shows the rule, never the value..github/workflows/stale-deploy-label.ymlruns onubuntu-latest.How to test
Start on a Linux machine with bash 5, Python 3.12,
uvandjq, in a checkout of this branch.uv run --with pyyaml --with pytest --python 3.12 -m pytest tests/ -q, the command CI runs. Expect276 passed. On macOS, whose bash 3.2 cannot parse theargocd-pr-env-deployscript, expect258 passed, 18 skipped.actionlint -colorat the repository root. Expect no output and exit code 0.git checkout origin/main -- build-push-ecr/action.yml, then the pytest command from step 1 ontests/test_build_push_ecr.py. Expect 8 failures. Restore the file withgit checkout HEAD -- build-push-ecr/action.yml.build-push-ecrwithoutbuilder. Expect a green run whose build step log still shows theaws ecr create-repositoryattempt and theremote-buildkit-agentbuilder.pr-envs.ymlwhile a cowork PR labelleddeployhas its head image inmindsdb-cowork-dev. Expect theargocd-pr-env-deploystep to logargocd app set pr-cowork-<PR number>withtags.cowork=development-head-<head SHA>.ubuntu-latest, run theExpose the GitHub Actions cache to docker buildxstep, and pushdevelopment-head-<head SHA>tomindsdb-cowork-dev. Re-run the job, and expect the build log to mark unchanged layersCACHED. Local mode has not run on a GitHub-hosted runner before, so this is the step most likely to find a problem.Notes for the reviewer
Merge order: mindsdb/terraform PR A, then this PR, then the mindsdb/deployer re-pin. mindsdb/anton, mindsdb/cowork and mindsdb/cowork-server call
build-push-ecr@mainwithbuilder: local. Their builds need this PR onmain, plus the roles and dev-tier repositories that PR A creates. mindsdb/deployer pinsargocd-pr-env-deployto331c0be, which has none of the new inputs, so it re-pins to this PR's merge commit before it merges.Once this PR is on
mainand a-devrepository exists, PR environments look for that repo's images there only. A cowork or cowork-server PR whose image was built intomindsdb-coworkormindsdb-cowork-serverfails the check until it builds again into the dev tier. The action never falls back to the other repository.Operator steps this PR needs.
gh api repos/mindsdb/github-actions/commits/main --jq .sha.gh api repos/moby/buildkit/releases/latest --jq .tag_nameand its digest fromdocker buildx imagetools inspect moby/buildkit:<version>, then change both together inbuild-push-ecr/action.yml. Nothing bumps that pin automatically, and the same holds forsnyk-versioninsnyk-docker-scan.Rollback: revert the merge commit on
main. Private callers see no difference, because they run the defaultremotepath either way. Once anton, cowork or cowork-server build withbuilder: local, revert their build changes first. The reverted action has nobuilderinput, so it would run the remote path on a GitHub-hosted runner, which cannot reach the in-cluster BuildKit service. The deployer keeps working, because it pins a commit.The pins change nothing that runs today. Each pinned SHA is the commit that both the floating tag and the version tag point at today.
snyk/actions/setupwas onmaster, which has no tag, so it moves tov1.0.0, whosesetup/tree is identical tomaster's. Snyk CLIv1.1307.4is the current release. BuildKitv0.33.1has the index digest that buildx's defaultbuildx-stable-1tag points at today, andremotemode keeps that default.Local mode exports the cache service's token to the rest of the caller job.
docker buildxreads the cache URL and token fromACTIONS_*variables, which the runner gives to actions but not torun:steps.crazy-max/ghaction-github-runtimeexports them. The token prints as***, because the runner'sWorker.csmasks every endpoint credential. In a job that grantsid-token: write,ScriptHandler.csalready gives everyrun:step that same token asACTIONS_ID_TOKEN_REQUEST_TOKEN. Local mode requires that grant, so it exposes nothing new.stale-deploy-label.ymlmoves every caller's prune job to GitHub-hosted runners. The job needs onlyghand the caller's token. Private callers run it there too.Deliberate omissions.
zizmorstill reports 12template-injectionfindings in these composites: 7 inbuild-push-ecr, 3 insnyk-docker-scanand 2 insetup-env. Each is on a${{ }}expression that was already in itsrun:block. Movingextra-build-argsinto an environment variable changes how bash parses quotes in it, and remote mode had to stay identical for every private caller.ignore-error=true, so a cache-service error fails a local build instead of skipping the cache write.argocd-pr-env-deploytests hand GitHub API responses to thecurlstub as raw dicts, because the stub serves them verbatim as JSON. A typed model would only be serialized back.tests/test_build_push_ecr.pyrepeats thestep()helper fromtests/test_notify_pipeline_status.py. A shared helper can wait for a third copy.Verified locally
pytest tests/ -qon Ubuntu 24.04 x86_64 (bash 5.2.21, jq 1.7, Python 3.12.3)argocd-pr-env-deployscript, which needs bash 4mainversion, then its tests runbuild-push-ecr: 8 of 10 fail.argocd-pr-env-deploy: 21 of 22 fail. The pin tests fail forsnyk-docker-scan(2) andsetup-env(1). Dropping--max-items 1alone fails 5 testsmainagainst this branch, for all 29build-push-ecrcalls in 23 workflow files across 15 repositories that code search found, for development, staging and production, with and without a PR head SHAawsordockerargv or inGITHUB_OUTPUT. The one added step runs only whenbuilder == 'local'actionlint1.7.12scripts/workflow_graph.py --allow-external-reusableszizmor1.28.0 offline on the changed composites and.github/workflows,mainagainst this branchunpinned-usesfindings are gone, and nothing is new. Both sides splice the same 37${{ }}expressions intorun:blockszizmor1.28.0 online audits, from an earlier run on these pinsimpostor-commit,ref-version-mismatchorknown-vulnerable-actionsfindingsgh apion each pinned action, 2026-10-03snyk/actionshas the samesetup/tree atmasterandv1.0.0docker buildx imagetools inspect, 2026-10-03moby/buildkit:v0.33.1has the pinned index digest, andbuildx-stable-1points at the same digest.v0.33.1is the latest BuildKit releasev1.1307.4is the latest release (2026-09-23). An earlier check found its sha256 equal to thelatestdownload on both Snyk hostsaws ecr describe-imageson an existing repository, read-only, from an earlier run--max-items 1: one image and exit 0. Without it, one page holds 100 images, and the CLI fetches every pagemainNodeScriptActionHandler.csgives actionsACTIONS_RUNTIME_TOKEN,ScriptHandler.csgivesrun:steps the same token when the job can mint OIDC tokens, andWorker.csmasks itorigin/mainShips with
Part of ENG-2000. This PR changes shared CI actions, so it carries no
Deploys:lines and nodeploylabel.Merge order
argocd-pr-env-deployto this PR's merge commit. 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. mindsdb/minds_python_sdk#90, 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: