The container ran a developer's own `hooks/root.sh` and `hooks/user.sh`
while the image was built, and nothing of theirs afterwards. A
customization that has to start something, or to reach the source tree,
had nowhere to run: the tree is not mounted during the build, and the one
lifecycle command the configuration used was spoken for by the status
report.
Run an optional script per phase through `run-hooks.sh`, and offer five:
`initialize` on the host, `build` in the image, and `post-create`,
`post-start` and `post-attach` in the container. A failing `build` hook
fails the image build, because an image whose customizations did not
apply is quietly wrong; the rest are reported and otherwise ignored, so
that a typo in a personal hook cannot leave its author unable to open the
container in order to fix it.
The two build hooks become one, running as the container user, who may
`sudo`: one hook that reaches either user is simpler to write against
than two that each reach one. Every hook is told where it lives, and
every hook but the build one is given a directory to keep state in,
beside the hooks rather than among them because state is written by
whatever they start rather than by hand. `.dockerignore` keeps that
directory out of the build context, which state written as `root` would
otherwise make unreadable.
A tool that opens the container picks a workspace path of its own when
the configuration names none, and `.gitlab/ci/devcontainer-run.sh` mounts
the source tree at a path it spells itself. The two disagreed: an editor
opened the tree at `/workspaces/cmake` while CI used the
`/home/cmake-dev/workspace` that `create_user.sh` prepares for it. Say
which one it is, so that a path means the same thing everywhere.
Provide the analysis tools our CI images carry: `clang-tidy` to run the
checks in `.clang-tidy`, `clang-tools` for `scan-build`, and `clazy`, the
compiler our Clazy job builds with.
Name the version of `clang-tidy` our checks are written against rather
than install Ubuntu's unversioned package, which is a release behind and
reports `cmake-use-cmsys-fstream` where it should not. `CMakeLists.txt`
searches for `clang-tidy` only under the unversioned name, so link the
version installed by name to it, as we already do for `clang-format`.
`clazy` and `clang-tools` remain whatever versions Ubuntu carries rather
than the ones the CI image does, so expect their diagnostics to differ
from those jobs'. CMake's own clang-tidy checks are not available either
way: `CMake_USE_CLANG_TIDY_MODULE` needs `Utilities/ClangTidyModule` built
against Clang's development files, which are not installed here.
Suggested-by: Tyler Yankee <tyler.yankee@kitware.com>
Provide `clang` for developers who prefer to build with it rather than
with the default `g++`, and `valgrind` to run CMake and its tests under a
memory checker. The sanitizer runtimes already come with both
compilers, so building with `-fsanitize=` needs nothing installed.
Name `gfortran` as well, without which Clang cannot link at all. Ubuntu
26.04 carries a Fortran-only `gcc-16` build that provides no C++ standard
library, `libopenmpi-dev` lists `gfortran-16` first among the
alternatives it accepts, and Clang selects the newest GCC installation it
finds. Naming `gfortran` lets `apt` satisfy that dependency with
`gfortran-15` instead, so the GCC 16 installation never appears.
Suggested-by: Tyler Yankee <tyler.yankee@kitware.com>
The sentence packed the two purposes of the mounts and their effect into
one clause chain, which was hard to follow. Split it in two.
Suggested-by: Tyler Yankee <tyler.yankee@kitware.com>
Configure `apt.kitware.com` in the development container so that `cmake`
is the latest CMake release rather than the older one Ubuntu carries, and
so that developers reach each further release through `apt-get` alone.
Fetch the repository's signing key, verified against the hash the
`Dockerfile` pins, and trust it just long enough to install
`kitware-archive-keyring`, which then provides the key, so that `apt`
follows the rotations Kitware makes to it. Interpolating the hash into
the step that fetches the key also busts the build cache when the hash
changes, so a rotation is picked up rather than served from an old layer.
Reaching either the key or the repository needs `curl` and a certificate
store, neither of which the base image carries, so install them first.
Install `glab`, the GitLab CLI, and `glab-axi`, an agent-ergonomic
wrapper around it, in the development container.
Default `GITLAB_HOST` to `gitlab.kitware.com` so that both address our
GitLab instance without further configuration, persist the `glab`
configuration directory on a volume so that a credential need be created
only once, and pass a `GITLAB_TOKEN` or `GITLAB_CLIENT_ID` from the host
through to the container.
Neither tool can authenticate on its own, so check for a working
credential each time a tool attaches to the container and print what
remains to be set up if there is none.
Provide a `.devcontainer` definition, following the Dev Container
Specification, to give contributors a ready-made Linux environment with
the tools needed to build CMake, run its test suite, build its
documentation, and satisfy its style rules.
Base the container on Ubuntu, which offers the broadest ecosystem of
packages and tooling for development, including a recent `cmake` and the
`clang-format` version our style rules require, exactly. Mirror the
package lists of the Debian image our CI infrastructure uses, section by
section, so that the dependencies available closely match the ones
against which merge requests are tested. Build the image the way the
images under `.gitlab/ci/docker/` are built: bind mount the package
lists and the installation script rather than copying them in, and cache
the package lists and downloaded archives so that a rebuild fetches only
what has changed.
Run optional `.devcontainer/hooks/root.sh` and
`.devcontainer/hooks/user.sh` scripts, both ignored by Git, so that
developers may customize the container without modifying tracked files.
Document usage in a new `Help/dev/devcontainer.rst`.
Fixes: #28043