Install the Python formatter in the build image (#8286) - #8287
Open
hannes-ucsc wants to merge 15 commits into
Open
hannes-ucsc wants to merge 15 commits into
hannes-ucsc wants to merge 15 commits into
Conversation
hannes-ucsc
force-pushed
the
issues/hannes-ucsc/8286-7825-eliminate-pycharm-image
branch
from
September 5, 2026 23:29
46e8118 to
2765c7d
Compare
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## develop #8287 +/- ##
===========================================
- Coverage 84.79% 84.79% -0.01%
===========================================
Files 168 168
Lines 25196 25201 +5
===========================================
+ Hits 21365 21369 +4
- Misses 3831 3832 +1 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
hannes-ucsc
force-pushed
the
issues/hannes-ucsc/8286-7825-eliminate-pycharm-image
branch
2 times, most recently
from
September 7, 2026 23:50
01d9b3d to
660a2cb
Compare
Nothing refers to `ucscgi/azul-pycharm` anymore, now that the formatter is installed in the image built from the Dockerfile in this directory. The checklist items for building and publishing it go with it, as does the one asking for its unused tags to be removed after a promotion, which the PR template derives from the images marked as custom. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…8286) The GitHub build has no credentials for the mirror and pulls images from the upstream registry instead. The digest of the base image is that of the image in the mirror, and digests of multi-platform images differ between the mirror and the upstream registry, so that reference can't be used there. Referring to the image by tag would work, but only by giving up the pinning that the digest provides everywhere else, letting a rebuilt tag change the base image without anything in this repository changing. Track the upstream digest in `environment.boot` as well, next to the mirrored one, and refer to that in the GitHub build. Tracking the image twice leaves the existing name saying nothing about which of the two it is, so it becomes `azul_python_image_mirrored`. The build argument keeps the unqualified name, because which of the two references a build passes is the caller's choice, and a build that pulls from the upstream registry shouldn't pass its reference in an argument named after the mirror. `common.mk` defaults that choice to the mirrored reference. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Docker's apt repository is now configured at the top of the file, ahead of the single `apt-get update`, so that one index serves the three installs below it. The packages the base image ships are no longer upgraded. That image is pinned to a digest that is bumped every other week, which is what keeps them current, and leaving them at the versions that digest ships makes the content of this image a function of that digest. Bumping `azul_image_version` fetches a new index, which is necessary when a build fails because a version in the cached index is no longer available from the repository. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The comment preceding `azul_pycharm_version` now states what to do after changing it, and the checklist item in the issue template for the biweekly upgrade refers to that comment instead of naming the commands itself. Establishing which parts of the archive to extract is involved enough to be described separately, by a skill, which applies only when `make format` fails after the version was bumped. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The line for the downloaded archive used to be selected with `grep`, which interpreted the file name as a regular expression and matched it anywhere in a line. Neither is a problem for the names and files involved today, but that is a property of what those files happen to contain. `sha256sum --ignore-missing` verifies every file the checksum file lists that is actually present, which is the one just downloaded, and fails when none is. That also removes the only pipeline from each of the four instructions, and with it the need for `pipefail`. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Two instructions left files there. The post-install script of the package providing the JRE runs a JVM, which creates a directory for its performance data, and uv leaves a lock file per package it installs. While at it, remove each kind of leftover with the least forceful form that does the job: a plain `rm` for files, `rm -r` for the empty directory the JVM leaves, and `rm -rf` only for directories that actually have something in them. The instruction installing the AWS CLI used one `rm -rf` for two files and a directory. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The archive was downloaded into a directory in the image root, unpacked in full, the binary moved out of it and the directory removed again. Download it to /tmp, like every other archive, and extract just the binary to its destination. The license file in the archive was discarded before and still is. `curl` now fails on a response that isn't the archive, instead of saving it and leaving `unzip` to complain about it, and the `pipefail` around the extraction is gone, having had no pipeline to watch over. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The archive holds a Python runtime and the data files of botocore, thousands of files in all, and `unzip` named every one of them. In the most recent build on GitHub those lines accounted for 7562 of the 32091 lines the job logged. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
There are now three targets, each assuming more than the one before it. `__format` assumes the formatter is installed where it runs, and is what the GitLab build invokes inside a container from the image. `_format` assumes only that the image exists, and runs `__format` in a container from it. `format` assumes nothing and builds the image first. The middle one is for a build that obtains the image some other way than by building it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Building the image accounts for most of the two minutes the GitHub build spends formatting. Push it to the registry that GitHub hosts, under a tag naming the hash of everything that determines its contents, and pull it in builds whose inputs hash the same. Compressed, the image is 859MB, so a build that finds one trades the build for a download of that size. The image is only ever reused by a tree that would have produced an equivalent one, and a build that finds none builds and pushes one, so the first build of this branch populates the registry. Note that this creates a package that is visible to everyone, this repository being public. Nothing in that registry expires by itself, which is why the build prunes it, keeping the five most recent versions. Failing to prune doesn't fail the build. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Running the formatter as the invoking user requires that user to have an entry in /etc/passwd and a home directory to write to. Both were assembled in a temporary directory in the project root and mounted into the container. Setting HOME is enough: the JVM falls back to it when it can't look the user up. It names /tmp, the one directory in the image that any user can write to, so the caches and indexes PyCharm leaves there are discarded with the container. Nothing is created in the project root anymore, so nothing has to be removed from it either, and the ignore rule for it is obsolete. The project is mounted at /azul now, the path the runner image uses for it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The dev image carried a virtual environment at /build/.venv that every GitLab job had to activate. `UV_PROJECT_ENVIRONMENT` points uv at the image's own Python installation instead, the way the lambda images already do it. Nothing activates anything, and a virtual environment in a bind-mounted project root can't be mistaken for the one to use. The `check_venv` and `check_python` targets consult whichever of the two variables naming an environment is set, so they keep their meaning on a developer machine and acquire one in a container. Both images use /azul for the directory that holds the lock file while uv reads it, matching the path at which the project is mounted for formatting. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Resolving a path to a host path fails on a host, where there is no container whose mounts it could be resolved against, and in a container whose mounts it falls outside of. Both are documented outcomes, and the caller handles them by using the path as it is. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
hannes-ucsc
force-pushed
the
issues/hannes-ucsc/8286-7825-eliminate-pycharm-image
branch
from
September 8, 2026 17:32
660a2cb to
a6e74ff
Compare
Installing PyCharm makes the image take longer to build. A build with a cold layer cache took 298s of the 360s the stage allowed, close enough that a slower runner, a larger base image or a bigger PyCharm release would exceed it, and the failure would look like an infrastructure problem rather than a lack of time. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.
Linked issues: #8286
Checklist
Author
developissues/<GitHub handle of author>/<issue#>-<slug>1 when the issue title describes a problem, the corresponding PR
title is
Fix:followed by the issue titleAuthor (partiality)
ptag to titles of partial commitspartialor completely resolves all linked issuespartiallabelAuthor (reindex)
rtag to commit title or the changes introduced by this PR will not require reindexing of any deploymentreindex:devor the changes introduced by it will not require reindexing ofdevreindex:anvildevor the changes introduced by it will not require reindexing ofanvildevreindex:anvilprodor the changes introduced by it will not require reindexing ofanvilprodreindex:prodor the changes introduced by it will not require reindexing ofprodreindex:partialand its description documents the specific reindexing procedure fordev,anvildev,anvilprodandprodor requires a full reindex or carries none of the labelsreindex:dev,reindex:anvildev,reindex:anvilprodandreindex:prodAuthor (mirror)
mirror:devor the changes introduced by it will not require mirroring ofdevmirror:anvildevor the changes introduced by it will not require mirroring ofanvildevmirror:anvilprodor the changes introduced by it will not require mirroring ofanvilprodmirror:prodor the changes introduced by it will not require mirroring ofprodmirror:partialand its description documents the specific mirroring procedure fordev,anvildev,anvilprodandprodor requires a full mirroring or carries none of the labelsmirror:dev,mirror:anvildev,mirror:anvilprodandmirror:prodAuthor (API changes)
APIor this PR does not modify a REST APIa(A) tag to commit title for backwards (in)compatible changes or this PR does not modify a REST APIapp.pyor this PR does not modify a REST APIAuthor (upgrading deployments)
make docker_images.jsonand committed the resulting changes or this PR does not modifyazul_docker_images, or any other variables referenced in the definition of that variableutag to commit title or this PR does not require upgrading deploymentsupgradeor does not require upgrading deploymentsdeploy:sharedor does not modifydocker_images.json, and does not require deploying thesharedcomponent for any other reasondeploy:gitlabor does not require deploying thegitlabcomponentdeploy:runneror does not require deploying therunnerimageAuthor (hotfixes)
Ftag to main commit title or this PR does not include permanent fix for a temporary hotfixanvilprodandprod) have temporary hotfixes for any of the issues linked to this PRAuthor (before every review)
develop, squashed fixups from prior reviewsmake requirements_updateor this PR does not modifypyproject.tomlRtag to commit title or this PR does not modifyuv.lockreqsor does not modifyuv.lockmake integration_testpasses in personal deployment or this PR does not modify functionality that could affect the IT outcomePeer reviewer (after approval)
Note that after requesting changes, the PR must be assigned to only the author.
System administrator (after approval)
demoorno demono demono sandboxN reviewslabel is accurateOperator
reindex:…labels andrcommit title tagmirror:…labelsno demodevelopOperator (deploy
.sharedand.gitlabcomponents)_select dev.shared && CI_COMMIT_REF_NAME=develop make -C terraform/shared apply_keep_unusedor this PR is not labeleddeploy:shared_select dev.gitlab && CI_COMMIT_REF_NAME=develop make -C terraform/gitlab apply(an error from _login_docker_gitlab is benign if the instance was stopped for backup) or this PR is not labeleddeploy:gitlab_select anvildev.shared && CI_COMMIT_REF_NAME=develop make -C terraform/shared apply_keep_unusedor this PR is not labeleddeploy:shared_select anvildev.gitlab && CI_COMMIT_REF_NAME=develop make -C terraform/gitlab apply(an error from _login_docker_gitlab is benign if the instance was stopped for backup) or this PR is not labeleddeploy:gitlabdeploy:gitlabdeploy:gitlabSystem administrator (post-deploy of
.gitlabcomponent)dev.gitlabare complete or this PR is not labeleddeploy:gitlabanvildev.gitlabare complete or this PR is not labeleddeploy:gitlabOperator (deploy runner image)
_select dev.gitlab && make -C terraform/gitlab/runneror this PR is not labeleddeploy:runner_select anvildev.gitlab && make -C terraform/gitlab/runneror this PR is not labeleddeploy:runnerOperator (sandbox build)
sandboxlabel or PR is labeledno sandboxdevor PR is labeledno sandboxanvildevor PR is labeledno sandboxsandboxdeployment or PR is labeledno sandboxanvilboxdeployment or PR is labeledno sandboxsandboxdeployment or PR is labeledno sandboxanvilboxdeployment or PR is labeledno sandboxsandboxor this PR is not labeledupgrade, or upgrade instructions do not apply tosandboxanvilboxor this PR is not labeledupgrade, or upgrade instructions do not apply toanvilboxsandbox, deleted the catalogs specified in the notes or this PR is missing either thereindex:partialor thereindex:devlabel, or bothanvilbox, deleted the catalogs specified in the notes or this PR is missing either thereindex:partialor thereindex:anvildevlabel, or bothsandbox, deindexed the sources sepcified in the notes or this PR is missing either thereindex:partialor thereindex:devlabel, or bothanvilbox, deindexed the sources sepcified in the notes or this PR is missing either thereindex:partialor thereindex:anvildevlabel, or bothsandbox, indexed the sources specified in the notes or this PR is missing either thereindex:partialor thereindex:devlabel, or bothanvilbox, indexed the sources specified in the notes or this PR is missing either thereindex:partialor thereindex:anvildevlabel, or bothsandbox, indexed the catalogs specified in the notes or this PR is missing either thereindex:partialor thereindex:devlabel, or bothanvilbox, indexed the catalogs specified in the notes or this PR is missing either thereindex:partialor thereindex:anvildevlabel, or bothsandboxor this PR is not labeledreindex:devor it is labeled reindex:partialanvilboxor this PR is not labeledreindex:anvildevor it is labeled reindex:partialsandboxor this PR is not labeledreindex:devanvilboxor this PR is not labeledreindex:anvildevsandboxor this PR is not labeledmirror:devanvilboxor this PR is not labeledmirror:anvildevsandboxor this PR is not labeledmirror:devanvilboxor this PR is not labeledmirror:anvildevOperator (merge the branch)
pif the PR is also labeledpartialOperator (main build)
devanvildevdevdevanvildevanvildevdevor this PR is not labeledupgrade, or upgrade instructions do not apply todevanvildevor this PR is not labeledupgrade, or upgrade instructions do not apply toanvildevupgrade, or upgrade instructions do not apply to personal deployments_select dev.shared && make -C terraform/shared applyor this PR is not labeleddeploy:shared_select anvildev.shared && make -C terraform/shared applyor this PR is not labeleddeploy:shareddevanvildevOperator (reindex)
dev, deleted the catalogs specified in the notes or this PR is missing either thereindex:partialor thereindex:devlabel, or bothanvildev, deleted the catalogs specified in the notes or this PR is missing either thereindex:partialor thereindex:anvildevlabel, or bothdev, deindexed the sources sepcified in the notes or this PR is missing either thereindex:partialor thereindex:devlabel, or bothanvildev, deindexed the sources sepcified in the notes or this PR is missing either thereindex:partialor thereindex:anvildevlabel, or bothdev, indexed the sources specified in the notes or this PR is missing either thereindex:partialor thereindex:devlabel, or bothanvildev, indexed the sources specified in the notes or this PR is missing either thereindex:partialor thereindex:anvildevlabel, or bothdev, indexed the catalogs specified in the notes or this PR is missing either thereindex:partialor thereindex:devlabel, or bothanvildev, indexed the catalogs specified in the notes or this PR is missing either thereindex:partialor thereindex:anvildevlabel, or bothdevor this PR is not labeledreindex:devor it is labeled reindex:partialanvildevor this PR is not labeledreindex:anvildevor it is labeled reindex:partialdevor this PR is not labeledreindex:devor it is labeled reindex:partialanvildevor this PR is not labeledreindex:anvildevor it is labeled reindex:partialdevor this PR is not labeledreindex:devor it is labeled reindex:partialanvildevor this PR is not labeledreindex:anvildevor it is labeled reindex:partialdevor this PR is not labeledreindex:devdevor this PR is not labeledreindex:devdeploy_browserjob in the GitLab pipeline for this PR indevor this PR is not labeledreindex:devanvildevor this PR is not labeledreindex:anvildevdeploy_browserjob in the GitLab pipeline for this PR inanvildevor this PR is not labeledreindex:anvildevOperator (mirroring)
devor this PR is not labelledmirror:devanvildevor this PR is not labelledmirror:anvildevdevor this PR is not labelledmirror:devanvildevor this PR is not labelledmirror:anvildevdevor this PR is not labelledmirror:devanvildevor this PR is not labelledmirror:anvildevOperator
upgradeandAPIlabels to the next promotion PRs or this PR carries neither of these labelsdeploy:shared,deploy:gitlab,deploy:runner,reindex:partial,reindex:anvilprod,reindex:prod,mirror:partial,mirror:anvilprodandmirror:prodlabels to the next promotion PRs or this PR carries none of these labelsdeploy:shared,deploy:gitlab,deploy:runner,reindex:partial,reindex:anvilprod,reindex:prod,mirror:partial,mirror:anvilprodandmirror:prodlabels, from the description of this PR to that of the next promotion PRs or this PR carries none of these labelsShorthand for review comments
Lline is too longWline wrapping is wrongQbad quotesFother formatting problem