Repository: pantsbuild/pants
Pull request #22094: Fixed various small VSCode dev container issues
Author: login: kdowney-lot49
Labels: name: release-notes:not-required
description: [CI] PR doesn't require mention in release notes
Created: 2025-03-16T13:29:33Z | Merged: 2025-03-31T01:42:25Z

- Ensured hyperfine installs amd64 or arm64 appropriately based on platform
- Ensured local `.git` settings correct for smooth operation in container
- Installed missing Go dependency needed for tests
- Installed missing Python 3.9.x and 3.10.x needed for tests
- Fixed tests that were failing in dev container or under ARM64 specifically

#22092 


Diff:
diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile
index 8ba01f90f42..ba24d1cf079 100644
--- a/.devcontainer/Dockerfile
+++ b/.devcontainer/Dockerfile
@@ -1,13 +1,20 @@
-FROM mcr.microsoft.com/devcontainers/rust:1
+FROM mcr.microsoft.com/devcontainers/base:noble
 
-RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
+RUN arch=$(arch | sed s/aarch64/arm64/ | sed s/x86_64/amd64/) && apt-get update && export DEBIAN_FRONTEND=noninteractive \
     # Install prerequisites.
     && apt-get install --no-install-recommends -y build-essential clang libffi-dev libfuse-dev libssl-dev protobuf-compiler python3-dev \
+    ca-certificates software-properties-common lz4 \
     # Install `hyperfine` and `dbg` for debugging and benchmarking.
     # See: https://www.pantsbuild.org/stable/docs/contributions/development/debugging-and-benchmarking
     && apt-get install --no-install-recommends -y gdb python3-dbg \
-    && curl -Lo /tmp/hyperfine.deb https://github.com/sharkdp/hyperfine/releases/download/v1.19.0/hyperfine_1.19.0_amd64.deb \
-    && sudo dpkg -i /tmp/hyperfine.deb \
+    && curl -Lo /tmp/hyperfine.deb https://github.com/sharkdp/hyperfine/releases/download/v1.19.0/hyperfine_1.19.0_${arch}.deb \
+    && dpkg -i /tmp/hyperfine.deb \
+    # Add deadsnakes PPA and install Python versions.
+    && add-apt-repository ppa:deadsnakes/ppa \
+    && apt-get update \
+    && apt-get install --no-install-recommends -y python3.8 python3.8-dev python3.8-venv \
+        python3.9 python3.9-dev python3.9-venv \
+        python3.10 python3.10-dev python3.10-venv \
     # Cleanup.
     && rm -rf /tmp/* \
     && apt-get autoremove -y \
diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
index 27af0ccfe6b..0adcc547f94 100644
--- a/.devcontainer/devcontainer.json
+++ b/.devcontainer/devcontainer.json
@@ -6,9 +6,16 @@
     },
     "features": {
       "ghcr.io/devcontainers/features/docker-in-docker:2": {},
+      "ghcr.io/devcontainers/features/go:1": {},
+      "ghcr.io/devcontainers/features/kubectl-helm-minikube:1": {
+        "minikube": "none"
+      },
       "ghcr.io/devcontainers/features/python:1": {
         "version": "3.11.11"
       },
+      "ghcr.io/devcontainers/features/rust:1": {
+        "profile": "complete"
+      },
       "ghcr.io/stuartleeks/dev-container-features/shell-history:0": {}
     },
     "customizations": {
@@ -37,6 +44,7 @@
             "editor.defaultFormatter": "rust-lang.rust-analyzer",
             "editor.formatOnSave": true
           },
+          "dev.containers.dockerCredentialHelper": false,
           "python.analysis.extraPaths": ["src/python"],
           "black-formatter.args": ["--config=pyproject.toml"],
           "flake8.args": ["--config=build-support/flake8/.flake8"],
@@ -71,6 +79,7 @@
         "type": "volume"
       }
     ],
+    "onCreateCommand": ".devcontainer/onCreateCommand.sh",
     "postCreateCommand": ".devcontainer/postCreateCommand.sh",
     "postStartCommand": ".devcontainer/postStartCommand.sh"
   }
diff --git a/.devcontainer/onCreateCommand.sh b/.devcontainer/onCreateCommand.sh
new file mode 100755
index 00000000000..03f5dedbfcd
--- /dev/null
+++ b/.devcontainer/onCreateCommand.sh
@@ -0,0 +1,48 @@
+#!/usr/bin/env bash
+script_dir="$(dirname -- "$(realpath -- "$0")")"
+workspace_dir="$(dirname "${script_dir}")"
+
+# helper functions
+set_git_config_if_not_set() {
+	local key="$1"
+	local value="$2"
+
+	# Split the value by comma and iterate over each part
+	IFS=',' read -ra ADDR <<<"$value"
+	for val in "${ADDR[@]}"; do
+		if ! git config --global --get "$key" | grep -q "$val"; then
+			git config --global --add "$key" "$val"
+		fi
+	done
+}
+
+set_git_user_info() {
+	# Get the credentials from the Git credential helper
+	CREDENTIAL_REQUEST=$(
+		cat <<EOF
+protocol=https
+host=github.com
+EOF
+	)
+	CREDENTIALS=$(git credential fill <<<"$CREDENTIAL_REQUEST")
+
+	# Extract the username and password (which is the PAT)
+	USERNAME=$(echo "$CREDENTIALS" | grep 'username=' | cut -d '=' -f2)
+	PASSWORD=$(echo "$CREDENTIALS" | grep 'password=' | cut -d '=' -f2)
+
+	# Fetch the user information associated with the current credentials
+	USER_INFO=$(curl -s -u "$USERNAME:$PASSWORD" https://api.github.com/user)
+	USER_EMAILS=$(curl -s -u "$USERNAME:$PASSWORD" https://api.github.com/user/public_emails)
+
+	# Extract the name and email address from the user information
+	NAME=$(echo "$USER_INFO" | jq -r '.name')
+	EMAIL=$(echo "$USER_EMAILS" | jq -r '.[0].email')
+
+	# Set the Git user.email and user.name configurations
+	set_git_config_if_not_set user.email "$EMAIL"
+	set_git_config_if_not_set user.name "$NAME"
+}
+
+# fix Git safe.directory and ensure user.name and user.email set correctly
+set_git_config_if_not_set safe.directory "${workspace_dir}"
+set_git_user_info
diff --git a/.devcontainer/postCreateCommand.sh b/.devcontainer/postCreateCommand.sh
index f3d254db811..02e325f6819 100755
--- a/.devcontainer/postCreateCommand.sh
+++ b/.devcontainer/postCreateCommand.sh
@@ -8,6 +8,9 @@ echo "Installing local git hooks..."
 # See: https://www.pantsbuild.org/stable/docs/contributions/development/setting-up-pants#step-3-set-up-a-pre-push-git-hook
 build-support/bin/setup.sh
 
+echo "Upgrading pip..."
+pip install --upgrade pip
+
 echo "Installing py-sy and memray for CPU and Memory profiling..."
 # See: https://www.pantsbuild.org/stable/docs/contributions/development/debugging-and-benchmarking
 pip install py-spy memray
diff --git a/src/python/pants/backend/docker/util_rules/docker_build_context_test.py b/src/python/pants/backend/docker/util_rules/docker_build_context_test.py
index b42fa14b4dd..7ada9d48192 100644
--- a/src/python/pants/backend/docker/util_rules/docker_build_context_test.py
+++ b/src/python/pants/backend/docker/util_rules/docker_build_context_test.py
@@ -6,6 +6,7 @@
 import json
 import os
 import zipfile
+from platform import machine
 from textwrap import dedent
 from typing import Any, ContextManager
 
@@ -448,14 +449,26 @@ def test_packaged_pex_path(rule_runner: RuleRunner) -> None:
 
 
 def test_packaged_pex_environment(rule_runner: RuleRunner) -> None:
+    image = (
+        "python:3.8-buster@sha256:04c3f641c2254c229fd2f704c5199ff4bea57d26c1c29008ae3a4afddde98709"
+    )
+    if machine() == "x86_64":
+        platform = "linux_x86_64"
+        expected_dist = "psutil-5.9.2-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl"
+    elif machine() == "aarch64":
+        platform = "linux_arm64"
+        expected_dist = "psutil-5.9.2-cp38-cp38-linux_aarch64.whl"
+    else:
+        pytest.skip("This test only runs on amd64 and arm64.")
+
     rule_runner.write_files(
         {
             "BUILD": dedent(
-                """
+                f"""
               docker_environment(
                 name="python_38",
-                image="python:3.8-buster@sha256:bc4b9fb034a871b285bea5418cedfcaa9d2ab5590fb5fb6f0c42aaebb2e2c911",
-                platform="linux_x86_64",
+                image="{image}",
+                platform="{platform}",
                 python_bootstrap_search_path=["<PATH>"],
               )
 
@@ -490,7 +503,7 @@ def test_packaged_pex_environment(rule_runner: RuleRunner) -> None:
     rule_runner.write_digest(context.digest, path_prefix="contents")
     with zipfile.ZipFile(os.path.join(rule_runner.build_root, "contents", pex_file), "r") as zf:
         assert json.loads(zf.read("PEX-INFO"))["distributions"].keys() == {
-            "psutil-5.9.2-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
+            expected_dist,
         }
 
 
diff --git a/src/python/pants/backend/go/util_rules/cgo_test.py b/src/python/pants/backend/go/util_rules/cgo_test.py
index 244d461de4a..2ae2bf5a2c9 100644
--- a/src/python/pants/backend/go/util_rules/cgo_test.py
+++ b/src/python/pants/backend/go/util_rules/cgo_test.py
@@ -42,6 +42,7 @@
 from pants.engine.internals.native_engine import EMPTY_DIGEST
 from pants.engine.process import Process, ProcessResult
 from pants.testutil.rule_runner import QueryRule, RuleRunner
+from pants.testutil.skip_utils import skip_if_linux_arm64
 
 
 @pytest.fixture
@@ -502,6 +503,7 @@ def test_cgo_with_fortran_source(rule_runner: RuleRunner) -> None:
     assert result.stdout.decode() == "Answer: 42\n"
 
 
+@skip_if_linux_arm64
 @pytest.mark.no_error_if_skipped
 def test_cgo_with_embedded_static_library(rule_runner: RuleRunner) -> None:
     # gcc needed for linking
diff --git a/src/python/pants/backend/python/goals/package_pex_binary_integration_test.py b/src/python/pants/backend/python/goals/package_pex_binary_integration_test.py
index 26e607d3281..6dfca13c748 100644
--- a/src/python/pants/backend/python/goals/package_pex_binary_integration_test.py
+++ b/src/python/pants/backend/python/goals/package_pex_binary_integration_test.py
@@ -40,6 +40,7 @@
 from pants.testutil.python_interpreter_selection import skip_unless_python38_present
 from pants.testutil.python_rule_runner import PythonRuleRunner
 from pants.testutil.rule_runner import QueryRule
+from pants.testutil.skip_utils import skip_if_linux_arm64
 
 
 @pytest.fixture
@@ -254,6 +255,7 @@ def pex_executable(rule_runner: PythonRuleRunner) -> str:
     return os.path.join(rule_runner.build_root, expected_pex_relpath)
 
 
+@skip_if_linux_arm64
 @skip_unless_python38_present
 @pytest.mark.parametrize("target_type", ["files", "resources"])
 def test_complete_platforms(rule_runner: PythonRuleRunner, target_type: str) -> None:
diff --git a/src/python/pants/backend/rust/lint/rustfmt/BUILD b/src/python/pants/backend/rust/lint/rustfmt/BUILD
index 53e7346cf82..1b7e54676f6 100644
--- a/src/python/pants/backend/rust/lint/rustfmt/BUILD
+++ b/src/python/pants/backend/rust/lint/rustfmt/BUILD
@@ -3,6 +3,4 @@
 
 python_sources()
 
-python_tests(
-    name="tests",
-)
+python_tests(name="tests", extra_env_vars=["RUSTUP_HOME"])
diff --git a/src/python/pants/backend/rust/lint/rustfmt/rules_integration_test.py b/src/python/pants/backend/rust/lint/rustfmt/rules_integration_test.py
index 38aeeff78f1..4a88e07d5d4 100644
--- a/src/python/pants/backend/rust/lint/rustfmt/rules_integration_test.py
+++ b/src/python/pants/backend/rust/lint/rustfmt/rules_integration_test.py
@@ -68,7 +68,7 @@ def run_rustfmt(
     *,
     extra_args: list[str] | None = None,
 ) -> FmtResult:
-    rule_runner.set_options(extra_args or (), env_inherit={"PATH"})
+    rule_runner.set_options(extra_args or (), env_inherit={"PATH", "RUSTUP_HOME"})
     field_sets = [RustfmtFieldSet.create(tgt) for tgt in targets]
     input_sources = rule_runner.request(
         SourceFiles,
diff --git a/src/python/pants/backend/rust/util_rules/toolchains.py b/src/python/pants/backend/rust/util_rules/toolchains.py
index ade9492215d..887355708eb 100644
--- a/src/python/pants/backend/rust/util_rules/toolchains.py
+++ b/src/python/pants/backend/rust/util_rules/toolchains.py
@@ -84,6 +84,7 @@ class RustBinaryPathRequest:
 async def rust_binary_path(
     request: RustBinaryPathRequest, rustup: RustupBinary, rust_subsystem: RustSubsystem
 ) -> BinaryPath:
+    env = await environment_vars_subset(**implicitly(EnvironmentVarsRequest(["RUSTUP_HOME"])))
     which_result = await execute_process_or_raise(
         **implicitly(
             Process(
@@ -93,6 +94,7 @@ async def rust_binary_path(
                     f"--toolchain={rust_subsystem.toolchain}",
                     request.binary,
                 ),
+                env=env,
                 description=f"Find path to Rust binary `{request.binary}` in toolchain `{rust_subsystem.toolchain}`",
             )
         )
diff --git a/src/python/pants/testutil/skip_utils.py b/src/python/pants/testutil/skip_utils.py
index 05742846ac6..e746dc7e11a 100644
--- a/src/python/pants/testutil/skip_utils.py
+++ b/src/python/pants/testutil/skip_utils.py
@@ -6,6 +6,8 @@
 
 import pytest
 
+from pants.engine.platform import Platform
+
 
 @lru_cache
 def skip_if_command_errors(*args: str):
@@ -20,5 +22,17 @@ def empty_decorator(func):
     return empty_decorator
 
 
+def skip_if_linux_arm64(func):
+    """Skip the test if running on linux-arm64."""
+    if Platform.create_for_localhost() == Platform.linux_arm64:
+        return pytest.mark.skip(reason="Test cannot run on Linux ARM64. Skipping.")(func)
+    else:
+
+        def empty_decorator(func):
+            return func
+
+        return empty_decorator(func)
+
+
 requires_go = skip_if_command_errors("go", "version")
 requires_thrift = skip_if_command_errors("thrift", "-version")


Reviews:
user: login: kdowney-lot49
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-16T13:34:17Z
user: login: kdowney-lot49
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-16T13:35:03Z
user: login: benjyw
body: Looks good to me, thanks! I'll let @erjac77 have a word before merging.
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-16T16:14:34Z
user: login: benjyw
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-16T16:16:00Z
user: login: kdowney-lot49
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-16T17:31:07Z
user: login: erjac77
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-17T02:16:21Z
user: login: kdowney-lot49
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-23T12:04:51Z
user: login: kdowney-lot49
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-23T12:05:49Z
user: login: kdowney-lot49
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-23T12:06:11Z
user: login: kdowney-lot49
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-23T12:06:37Z
user: login: kdowney-lot49
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-23T12:07:37Z
user: login: kdowney-lot49
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-23T13:43:12Z
user: login: benjyw
body: Looks great to me. @erjac77 any comments?
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-29T02:57:24Z
user: login: erjac77
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-30T02:43:24Z
user: login: erjac77
body: LGTM

Thank you. This is a great addition!
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-30T02:49:43Z
user: login: benjyw
body: Thanks for these excellent fixes!
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-30T18:16:54Z

Review comments:
diff_hunk: @@ -1,15 +1,34 @@
 FROM mcr.microsoft.com/devcontainers/rust:1
path: .devcontainer/Dockerfile
user: login: kdowney-lot49
body: To discuss: I think it would be better to use the `base:noble` container (Ubuntu 24.04) and then install the `rust` feature because this is Debian base. Among other things, you cannot use the deadsnakes PPA to install older Python versions, which is what forced me to install for source inside the `Dockerfile`. But to avoid making too big a change I left that for another PR.
created_at: 2025-03-16T13:34:17Z
author_association: CONTRIBUTOR
diff_hunk: @@ -13,4 +13,5 @@ echo "Installing py-sy and memray for CPU and Memory profiling..."
 pip install py-spy memray
 
 echo "Bootstrapping Pants..."
+mkdir dist
path: .devcontainer/postCreateCommand.sh
user: login: kdowney-lot49
body: Might be able to take this out; not 100% sure it's required, but I saw issues when bootstrapping first build.
created_at: 2025-03-16T13:35:02Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,15 +1,34 @@
 FROM mcr.microsoft.com/devcontainers/rust:1
path: .devcontainer/Dockerfile
user: login: benjyw
body: Thanks for looking at this. @erjac77, any thoughts?
created_at: 2025-03-16T16:09:15Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,48 @@
+#!/usr/bin/env bash
+script_dir="$(dirname -- "$(realpath -- "$0")")"
+workspace_dir="$(dirname "${script_dir}")"
+
+# helper functions
+set_git_config_if_not_set() {
+	local key="$1"
+	local value="$2"
+
+	# Split the value by comma and iterate over each part
+	IFS=',' read -ra ADDR <<<"$value"
+	for val in "${ADDR[@]}"; do
+		if ! git config --global --get "$key" | grep -q "$val"; then
+			git config --global --add "$key" "$val"
+		fi
+	done
+}
+
+set_git_user_info() {
+	# Get the credentials from the Git credential helper
+	CREDENTIAL_REQUEST=$(
+		cat <<EOF
+protocol=https
+host=github.com
+EOF
+	)
+	CREDENTIALS=$(git credential fill <<<"$CREDENTIAL_REQUEST")
+
+	# Extract the username and password (which is the PAT)
+	USERNAME=$(echo "$CREDENTIALS" | grep 'username=' | cut -d '=' -f2)
+	PASSWORD=$(echo "$CREDENTIALS" | grep 'password=' | cut -d '=' -f2)
+
+	# Fetch the user information associated with the current credentials
+	USER_INFO=$(curl -s -u "$USERNAME:$PASSWORD" https://api.github.com/user)
+	USER_EMAILS=$(curl -s -u "$USERNAME:$PASSWORD" https://api.github.com/user/public_emails)
+
+	# Extract the name and email address from the user information
+	NAME=$(echo "$USER_INFO" | jq -r '.name')
+	EMAIL=$(echo "$USER_EMAILS" | jq -r '.[0].email')
+
+	# Set the Git user.email and user.name configurations
+	set_git_config_if_not_set user.email "$EMAIL"
+	set_git_config_if_not_set user.name "$NAME"
+}
path: .devcontainer/onCreateCommand.sh
user: login: benjyw
body: That was some fun BASHing.
created_at: 2025-03-16T16:14:04Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,15 +1,34 @@
 FROM mcr.microsoft.com/devcontainers/rust:1
path: .devcontainer/Dockerfile
user: login: benjyw
body: Makes sense to me to get pythons from deadsnakes instead of laboriously building them, and the rust toolchain is easy and relatively quick to install. 
created_at: 2025-03-16T16:16:00Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,15 +1,34 @@
 FROM mcr.microsoft.com/devcontainers/rust:1
path: .devcontainer/Dockerfile
user: login: kdowney-lot49
body: Yep, my proposal is to add this feature instead, similar to the Go and Python features already in there:

```
        "ghcr.io/devcontainers/features/rust:1": {},
```

Then can use a newer, base-only image, and I can add deadsnakes PPA config in the `Dockerfile`.
created_at: 2025-03-16T17:31:06Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,15 +1,34 @@
 FROM mcr.microsoft.com/devcontainers/rust:1
path: .devcontainer/Dockerfile
user: login: erjac77
body: Hey, I'm glad to see other people using the Dev container! Thanks for chipping in.

At the time, I first tried using the pre-built [Python image](mcr.microsoft.com/devcontainers/python) and installing Rust as a feature, but I ran into some issues (although I don't remember exactly what they were). So I decided to go the other way around. But if the problems are no longer present, I would be happy to switch to the pre-built Python image and install the Rust feature as you suggest.

In my opinion, using a pre-built image offers some advantages. Beyond Python and `git`, the pre-built image includes a number of Python tools, `zsh`, [Oh My Zsh!](https://ohmyz.sh/), a non-root `vscode` user with `sudo` access, and a set of common dependencies for development.

Note that you can also install [multiple versions of Python](https://github.com/devcontainers/images/tree/main/src/python#optional-installing-multiple-versions-of-python-in-the-same-image) with the pre-built image using `deadsnakes`. I think this solution should be favored.
created_at: 2025-03-17T01:35:11Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,15 +1,34 @@
 FROM mcr.microsoft.com/devcontainers/rust:1
 
-RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
+RUN arch=$(arch | sed s/aarch64/arm64/ | sed s/x86_64/amd64/) && apt-get update && export DEBIAN_FRONTEND=noninteractive \
path: .devcontainer/Dockerfile
user: login: erjac77
body: Nice improvement. I think you can also use `TARGETARCH` set by `buildx`.
Maybe something like:
`ARG ARCH=${TARGETARCH:-amd64}`
created_at: 2025-03-17T01:44:17Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,15 +1,34 @@
 FROM mcr.microsoft.com/devcontainers/rust:1
 
-RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
+RUN arch=$(arch | sed s/aarch64/arm64/ | sed s/x86_64/amd64/) && apt-get update && export DEBIAN_FRONTEND=noninteractive \
     # Install prerequisites.
     && apt-get install --no-install-recommends -y build-essential clang libffi-dev libfuse-dev libssl-dev protobuf-compiler python3-dev \
+    wget checkinstall libreadline-dev libncursesw5-dev libsqlite3-dev tk-dev libgdbm-dev libc6-dev libbz2-dev zlib1g-dev \
     # Install `hyperfine` and `dbg` for debugging and benchmarking.
     # See: https://www.pantsbuild.org/stable/docs/contributions/development/debugging-and-benchmarking
     && apt-get install --no-install-recommends -y gdb python3-dbg \
-    && curl -Lo /tmp/hyperfine.deb https://github.com/sharkdp/hyperfine/releases/download/v1.19.0/hyperfine_1.19.0_amd64.deb \
+    && curl -Lo /tmp/hyperfine.deb https://github.com/sharkdp/hyperfine/releases/download/v1.19.0/hyperfine_1.19.0_${arch}.deb \
     && sudo dpkg -i /tmp/hyperfine.deb \
     # Cleanup.
     && rm -rf /tmp/* \
     && apt-get autoremove -y \
     && apt-get clean all \
     && rm -rf /var/lib/apt/lists/*
+
+# Compile python from source to add 3.9.x
path: .devcontainer/Dockerfile
user: login: erjac77
body: Like I said, if we decide to change for the pre-built Python image instead of the Rust one, it supports installing [multiple versions of Python](https://github.com/devcontainers/images/tree/main/src/python#optional-installing-multiple-versions-of-python-in-the-same-image).
created_at: 2025-03-17T01:48:21Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,48 @@
+#!/usr/bin/env bash
path: .devcontainer/onCreateCommand.sh
user: login: erjac77
body: Not sure why this script is needed, because the Dev Containers extension provides out of the box support for using local Git credentials from inside a container. See [Sharing Git credentials with your container](https://code.visualstudio.com/remote/advancedcontainers/sharing-git-credentials).
created_at: 2025-03-17T01:50:31Z
author_association: CONTRIBUTOR
diff_hunk: @@ -13,4 +13,5 @@ echo "Installing py-sy and memray for CPU and Memory profiling..."
 pip install py-spy memray
 
 echo "Bootstrapping Pants..."
+mkdir dist
path: .devcontainer/postCreateCommand.sh
user: login: erjac77
body: Humm, maybe you saw the error with the lockfiles not present, but it is expected the first build. See [postStartCommand.sh](https://github.com/pantsbuild/pants/blob/main/.devcontainer/postStartCommand.sh).
created_at: 2025-03-17T01:52:06Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,15 +1,34 @@
 FROM mcr.microsoft.com/devcontainers/rust:1
 
-RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
+RUN arch=$(arch | sed s/aarch64/arm64/ | sed s/x86_64/amd64/) && apt-get update && export DEBIAN_FRONTEND=noninteractive \
     # Install prerequisites.
     && apt-get install --no-install-recommends -y build-essential clang libffi-dev libfuse-dev libssl-dev protobuf-compiler python3-dev \
+    wget checkinstall libreadline-dev libncursesw5-dev libsqlite3-dev tk-dev libgdbm-dev libc6-dev libbz2-dev zlib1g-dev \
path: .devcontainer/Dockerfile
user: login: erjac77
body: Can you tell me why these extra dependencies are needed?
created_at: 2025-03-17T01:57:12Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,48 @@
+#!/usr/bin/env bash
+script_dir="$(dirname -- "$(realpath -- "$0")")"
+workspace_dir="$(dirname "${script_dir}")"
+
+# helper functions
+set_git_config_if_not_set() {
+	local key="$1"
+	local value="$2"
+
+	# Split the value by comma and iterate over each part
+	IFS=',' read -ra ADDR <<<"$value"
+	for val in "${ADDR[@]}"; do
+		if ! git config --global --get "$key" | grep -q "$val"; then
+			git config --global --add "$key" "$val"
+		fi
+	done
+}
+
+set_git_user_info() {
+	# Get the credentials from the Git credential helper
+	CREDENTIAL_REQUEST=$(
+		cat <<EOF
+protocol=https
+host=github.com
+EOF
+	)
+	CREDENTIALS=$(git credential fill <<<"$CREDENTIAL_REQUEST")
+
+	# Extract the username and password (which is the PAT)
+	USERNAME=$(echo "$CREDENTIALS" | grep 'username=' | cut -d '=' -f2)
+	PASSWORD=$(echo "$CREDENTIALS" | grep 'password=' | cut -d '=' -f2)
+
+	# Fetch the user information associated with the current credentials
+	USER_INFO=$(curl -s -u "$USERNAME:$PASSWORD" https://api.github.com/user)
+	USER_EMAILS=$(curl -s -u "$USERNAME:$PASSWORD" https://api.github.com/user/public_emails)
+
+	# Extract the name and email address from the user information
+	NAME=$(echo "$USER_INFO" | jq -r '.name')
+	EMAIL=$(echo "$USER_EMAILS" | jq -r '.[0].email')
+
+	# Set the Git user.email and user.name configurations
+	set_git_config_if_not_set user.email "$EMAIL"
+	set_git_config_if_not_set user.name "$NAME"
+}
+
+# fix Git safe.directory and ensure user.name and user.email set correctly
+set_git_config_if_not_set safe.directory "${workspace_dir}"
path: .devcontainer/onCreateCommand.sh
user: login: erjac77
body: If needed, I usually run the this command in the `postCreateCommand.sh` script.

`git config --global --add safe.directory /source`

It may be a good idea to also set the workspace folder in `devcontainer.json`.

`"workspaceFolder": "/source",`
created_at: 2025-03-17T02:13:41Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,48 @@
+#!/usr/bin/env bash
path: .devcontainer/onCreateCommand.sh
user: login: kdowney-lot49
body: @erjac77 apologies for the late reply, I was in an offsite. This script isn't about setting Git credentials; it's for setting up .gitconfig inside the container with correct user name and email using those pre-bound git credentials. This helps ensure those settings *match* the current GitHub credentials, which if you have more than one account is not always the case, leading to commits tagged with the wrong name or email. What I've found in the past is that .gitconfig gets clobbered on rebuilds, and so on initial open after a rebuild you need to set up manually. It's nice to have; I am happy to remove from the PR if you prefer.
created_at: 2025-03-23T12:04:51Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,15 +1,34 @@
 FROM mcr.microsoft.com/devcontainers/rust:1
 
-RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
+RUN arch=$(arch | sed s/aarch64/arm64/ | sed s/x86_64/amd64/) && apt-get update && export DEBIAN_FRONTEND=noninteractive \
     # Install prerequisites.
     && apt-get install --no-install-recommends -y build-essential clang libffi-dev libfuse-dev libssl-dev protobuf-compiler python3-dev \
+    wget checkinstall libreadline-dev libncursesw5-dev libsqlite3-dev tk-dev libgdbm-dev libc6-dev libbz2-dev zlib1g-dev \
     # Install `hyperfine` and `dbg` for debugging and benchmarking.
     # See: https://www.pantsbuild.org/stable/docs/contributions/development/debugging-and-benchmarking
     && apt-get install --no-install-recommends -y gdb python3-dbg \
-    && curl -Lo /tmp/hyperfine.deb https://github.com/sharkdp/hyperfine/releases/download/v1.19.0/hyperfine_1.19.0_amd64.deb \
+    && curl -Lo /tmp/hyperfine.deb https://github.com/sharkdp/hyperfine/releases/download/v1.19.0/hyperfine_1.19.0_${arch}.deb \
     && sudo dpkg -i /tmp/hyperfine.deb \
     # Cleanup.
     && rm -rf /tmp/* \
     && apt-get autoremove -y \
     && apt-get clean all \
     && rm -rf /var/lib/apt/lists/*
+
+# Compile python from source to add 3.9.x
path: .devcontainer/Dockerfile
user: login: kdowney-lot49
body: I tried the base Ubuntu with Python and Rust features and that worked too. I am fine to use the base Python instead and then install Rust feature into it and then remove the deadsnakes installs in Dockerfile, I agree that's cleaner.
created_at: 2025-03-23T12:05:49Z
author_association: CONTRIBUTOR
diff_hunk: @@ -13,4 +13,5 @@ echo "Installing py-sy and memray for CPU and Memory profiling..."
 pip install py-spy memray
 
 echo "Bootstrapping Pants..."
+mkdir dist
path: .devcontainer/postCreateCommand.sh
user: login: kdowney-lot49
body: Removed in my local, I'll push again.
created_at: 2025-03-23T12:06:10Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,15 +1,34 @@
 FROM mcr.microsoft.com/devcontainers/rust:1
 
-RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
+RUN arch=$(arch | sed s/aarch64/arm64/ | sed s/x86_64/amd64/) && apt-get update && export DEBIAN_FRONTEND=noninteractive \
     # Install prerequisites.
     && apt-get install --no-install-recommends -y build-essential clang libffi-dev libfuse-dev libssl-dev protobuf-compiler python3-dev \
+    wget checkinstall libreadline-dev libncursesw5-dev libsqlite3-dev tk-dev libgdbm-dev libc6-dev libbz2-dev zlib1g-dev \
path: .devcontainer/Dockerfile
user: login: kdowney-lot49
body: These were for compiling Python from source. If as you suggested @erjac77 we switch to Python base container they can be removed.
created_at: 2025-03-23T12:06:37Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,48 @@
+#!/usr/bin/env bash
+script_dir="$(dirname -- "$(realpath -- "$0")")"
+workspace_dir="$(dirname "${script_dir}")"
+
+# helper functions
+set_git_config_if_not_set() {
+	local key="$1"
+	local value="$2"
+
+	# Split the value by comma and iterate over each part
+	IFS=',' read -ra ADDR <<<"$value"
+	for val in "${ADDR[@]}"; do
+		if ! git config --global --get "$key" | grep -q "$val"; then
+			git config --global --add "$key" "$val"
+		fi
+	done
+}
+
+set_git_user_info() {
+	# Get the credentials from the Git credential helper
+	CREDENTIAL_REQUEST=$(
+		cat <<EOF
+protocol=https
+host=github.com
+EOF
+	)
+	CREDENTIALS=$(git credential fill <<<"$CREDENTIAL_REQUEST")
+
+	# Extract the username and password (which is the PAT)
+	USERNAME=$(echo "$CREDENTIALS" | grep 'username=' | cut -d '=' -f2)
+	PASSWORD=$(echo "$CREDENTIALS" | grep 'password=' | cut -d '=' -f2)
+
+	# Fetch the user information associated with the current credentials
+	USER_INFO=$(curl -s -u "$USERNAME:$PASSWORD" https://api.github.com/user)
+	USER_EMAILS=$(curl -s -u "$USERNAME:$PASSWORD" https://api.github.com/user/public_emails)
+
+	# Extract the name and email address from the user information
+	NAME=$(echo "$USER_INFO" | jq -r '.name')
+	EMAIL=$(echo "$USER_EMAILS" | jq -r '.[0].email')
+
+	# Set the Git user.email and user.name configurations
+	set_git_config_if_not_set user.email "$EMAIL"
+	set_git_config_if_not_set user.name "$NAME"
+}
+
+# fix Git safe.directory and ensure user.name and user.email set correctly
+set_git_config_if_not_set safe.directory "${workspace_dir}"
path: .devcontainer/onCreateCommand.sh
user: login: kdowney-lot49
body: This is a timing issue. I found that it has to be onCreateCommand.sh, or you still get this spurious error in VSCode about setting `safe.directory` -- not sure if they changed something, I started seeing it last year.
created_at: 2025-03-23T12:07:37Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,15 +1,34 @@
 FROM mcr.microsoft.com/devcontainers/rust:1
path: .devcontainer/Dockerfile
user: login: kdowney-lot49
body: From what I understand, both the Rust and Python base images are based on Debian 12. I faced issues with outdated dependencies in Rust as well (it's why I couldn't use deadsnakes in the standard way) which is why I went with Ubuntu 24.04 (noble) and then added Rust and Python features with PPA for deadsnakes. What I am going to do is push that update to this PR today so you can look at it, and let me know your thoughts, @erjac77 .
created_at: 2025-03-23T13:43:12Z
author_association: CONTRIBUTOR
diff_hunk: @@ -790,39 +3351,213 @@ def test_cgo_with_embedded_static_library(rule_runner: RuleRunner) -> None:
                 google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
                 google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
                 google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+                google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
                 google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
                 google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
                 google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
                 google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
-                google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=
                 google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
-                gopkg.in/avro.v0 v0.0.0-20171217001914-a730b5802183 h1:PGIdqvwfpMUyUP+QAlAnKTSWQ671SmYjoou2/5j7HXk=
-                gopkg.in/avro.v0 v0.0.0-20171217001914-a730b5802183/go.mod h1:FvqrFXt+jCsyQibeRv4xxEJBL5iG2DDW5aeJwzDiq4A=
+                google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
+                google.golang.org/protobuf v1.29.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
+                google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
+                google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
+                gopkg.in/airbrake/gobrake.v2 v2.0.9 h1:7z2uVWwn7oVeeugY1DtlPAy5H+KYgB1KeKTnqjNatLo=
+                gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U=
+                gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc=
+                gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
                 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+                gopkg.in/check.v1 v1.0.0-20141024133853-64131543e789/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
                 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-                gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
                 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+                gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+                gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
+                gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
+                gopkg.in/cheggaaa/pb.v1 v1.0.25 h1:Ev7yu1/f6+d+b3pi5vPdRPc6nNtP1umSfcWiEfRqv6I=
+                gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
                 gopkg.in/errgo.v1 v1.0.0 h1:n+7XfCyygBFb8sEjg6692xjC6Us50TFRO54+xYUEwjE=
                 gopkg.in/errgo.v1 v1.0.0/go.mod h1:CxwszS/Xz1C49Ucd2i6Zil5UToP1EmyrFhKaMVbg1mk=
                 gopkg.in/errgo.v2 v2.1.0 h1:0vLT13EuvQ0hNvakwLuFZ/jYrLp5F3kcWHXdRggjCE8=
                 gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
+                gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
+                gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
+                gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2 h1:OAj3g0cR6Dx/R07QgQe8wkA9RNjB2u4i700xBkIT4e0=
+                gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo=
                 gopkg.in/httprequest.v1 v1.2.1 h1:pEPLMdF/gjWHnKxLpuCYaHFjc8vAB2wrYjXrqDVC16E=
                 gopkg.in/httprequest.v1 v1.2.1/go.mod h1:x2Otw96yda5+8+6ZeWwHIJTFkEHWP/qP8pJOzqEtWPM=
+                gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
+                gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
+                gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno=
+                gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
                 gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22 h1:VpOs+IwYnYBaFnrNAeB8UUWtL3vEUnzSCL1nVjPhqrw=
                 gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=
+                gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
+                gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
+                gopkg.in/resty.v1 v1.12.0 h1:CuXP0Pjfw9rOuY6EP+UvtNvt5DSqHpIxILZKT/quCZI=
+                gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
                 gopkg.in/retry.v1 v1.0.3 h1:a9CArYczAVv6Qs6VGoLMio99GEs7kY9UzSF9+LD+iGs=
                 gopkg.in/retry.v1 v1.0.3/go.mod h1:FJkXmWiMaAo7xB+xhvDF59zhfjDWyzmyAxiT4dB688g=
+                gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
+                gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
+                gopkg.in/square/go-jose.v2 v2.5.1 h1:7odma5RETjNHWJnR32wx8t+Io4djHE1PqxCFx3iiZ2w=
+                gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
+                gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
+                gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
+                gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
+                gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
                 gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
                 gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+                gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+                gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
                 gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-                gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
                 gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+                gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+                gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
+                gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
                 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-                gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
+                gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
                 gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+                gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+                gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+                gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
+                gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
+                gotest.tools/gotestsum v1.8.2 h1:szU3TaSz8wMx/uG+w/A2+4JUPwH903YYaMI9yOOYAyI=
+                gotest.tools/gotestsum v1.8.2/go.mod h1:6JHCiN6TEjA7Kaz23q1bH0e2Dc3YJjDUZ0DmctFZf+w=
+                gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk=
+                gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8=
+                gotest.tools/v3 v3.3.0 h1:MfDY1b1/0xN1CyMlQDac0ziEy9zJQd9CXBRRDHw2jJo=
+                gotest.tools/v3 v3.3.0/go.mod h1:Mcr9QNxkg0uMvy/YElmo4SpXgJKWgQvYrT7Kw5RzJ1A=
                 honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
-                honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc h1:/hemPrYIhOhy8zYrNj+069zDB68us2sMGsfkFJO0iZs=
+                honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+                honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
                 honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+                honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
+                honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
+                honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
+                honnef.co/go/tools v0.1.3 h1:qTakTkI6ni6LFD5sBwwsdSO+AQqbSIxOauHTTQKZ/7o=
+                honnef.co/go/tools v0.1.3/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las=
+                k8s.io/api v0.20.1/go.mod h1:KqwcCVogGxQY3nBlRpwt+wpAMF/KjaCc7RpywacvqUo=
+                k8s.io/api v0.20.4/go.mod h1:++lNL1AJMkDymriNniQsWRkMDzRaX2Y/POTUi8yvqYQ=
+                k8s.io/api v0.20.6/go.mod h1:X9e8Qag6JV/bL5G6bU8sdVRltWKmdHsFUGS3eVndqE8=
+                k8s.io/api v0.22.5 h1:xk7C+rMjF/EGELiD560jdmwzrB788mfcHiNbMQLIVI8=
+                k8s.io/api v0.22.5/go.mod h1:mEhXyLaSD1qTOf40rRiKXkc+2iCem09rWLlFwhCEiAs=
+                k8s.io/apimachinery v0.20.1/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU=
+                k8s.io/apimachinery v0.20.4/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU=
+                k8s.io/apimachinery v0.20.6/go.mod h1:ejZXtW1Ra6V1O5H8xPBGz+T3+4gfkTCeExAHKU57MAc=
+                k8s.io/apimachinery v0.22.1/go.mod h1:O3oNtNadZdeOMxHFVxOreoznohCpy0z6mocxbZr7oJ0=
+                k8s.io/apimachinery v0.22.5 h1:cIPwldOYm1Slq9VLBRPtEYpyhjIm1C6aAMAoENuvN9s=
+                k8s.io/apimachinery v0.22.5/go.mod h1:xziclGKwuuJ2RM5/rSFQSYAj0zdbci3DH8kj+WvyN0U=
+                k8s.io/apiserver v0.20.1/go.mod h1:ro5QHeQkgMS7ZGpvf4tSMx6bBOgPfE+f52KwvXfScaU=
+                k8s.io/apiserver v0.20.4/go.mod h1:Mc80thBKOyy7tbvFtB4kJv1kbdD0eIH8k8vianJcbFM=
+                k8s.io/apiserver v0.20.6/go.mod h1:QIJXNt6i6JB+0YQRNcS0hdRHJlMhflFmsBDeSgT1r8Q=
+                k8s.io/apiserver v0.22.5 h1:71krQxCUz218ecb+nPhfDsNB6QgP1/4EMvi1a2uYBlg=
+                k8s.io/apiserver v0.22.5/go.mod h1:s2WbtgZAkTKt679sYtSudEQrTGWUSQAPe6MupLnlmaQ=
+                k8s.io/client-go v0.20.1/go.mod h1:/zcHdt1TeWSd5HoUe6elJmHSQ6uLLgp4bIJHVEuy+/Y=
+                k8s.io/client-go v0.20.4/go.mod h1:LiMv25ND1gLUdBeYxBIwKpkSC5IsozMMmOOeSJboP+k=
+                k8s.io/client-go v0.20.6/go.mod h1:nNQMnOvEUEsOzRRFIIkdmYOjAZrC8bgq0ExboWSU1I0=
+                k8s.io/client-go v0.22.5 h1:I8Zn/UqIdi2r02aZmhaJ1hqMxcpfJ3t5VqvHtctHYFo=
+                k8s.io/client-go v0.22.5/go.mod h1:cs6yf/61q2T1SdQL5Rdcjg9J1ElXSwbjSrW2vFImM4Y=
+                k8s.io/code-generator v0.19.7 h1:kM/68Y26Z/u//TFc1ggVVcg62te8A2yQh57jBfD0FWQ=
+                k8s.io/code-generator v0.19.7/go.mod h1:lwEq3YnLYb/7uVXLorOJfxg+cUu2oihFhHZ0n9NIla0=
+                k8s.io/component-base v0.20.1/go.mod h1:guxkoJnNoh8LNrbtiQOlyp2Y2XFCZQmrcg2n/DeYNLk=
+                k8s.io/component-base v0.20.4/go.mod h1:t4p9EdiagbVCJKrQ1RsA5/V4rFQNDfRlevJajlGwgjI=
+                k8s.io/component-base v0.20.6/go.mod h1:6f1MPBAeI+mvuts3sIdtpjljHWBQ2cIy38oBIWMYnrM=
+                k8s.io/component-base v0.22.5 h1:U0eHqZm7mAFE42hFwYhY6ze/MmVaW00JpMrzVsQmzYE=
+                k8s.io/component-base v0.22.5/go.mod h1:VK3I+TjuF9eaa+Ln67dKxhGar5ynVbwnGrUiNF4MqCI=
+                k8s.io/cri-api v0.17.3/go.mod h1:X1sbHmuXhwaHs9xxYffLqJogVsnI+f6cPRcgPel7ywM=
+                k8s.io/cri-api v0.20.1/go.mod h1:2JRbKt+BFLTjtrILYVqQK5jqhI+XNdF6UiGMgczeBCI=
+                k8s.io/cri-api v0.20.4/go.mod h1:2JRbKt+BFLTjtrILYVqQK5jqhI+XNdF6UiGMgczeBCI=
+                k8s.io/cri-api v0.20.6/go.mod h1:ew44AjNXwyn1s0U4xCKGodU7J1HzBeZ1MpGrpa5r8Yc=
+                k8s.io/cri-api v0.23.1 h1:0DHL/hpTf4Fp+QkUXFefWcp1fhjXr9OlNdY9X99c+O8=
+                k8s.io/cri-api v0.23.1/go.mod h1:REJE3PSU0h/LOV1APBrupxrEJqnoxZC8KWzkBUHwrK4=
+                k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
+                k8s.io/gengo v0.0.0-20200428234225-8167cfdcfc14/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
+                k8s.io/gengo v0.0.0-20201113003025-83324d819ded h1:JApXBKYyB7l9xx+DK7/+mFjC7A9Bt5A93FPvFD0HIFE=
+                k8s.io/gengo v0.0.0-20201113003025-83324d819ded/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E=
+                k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
+                k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y=
+                k8s.io/klog/v2 v2.4.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y=
+                k8s.io/klog/v2 v2.9.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec=
+                k8s.io/klog/v2 v2.30.0 h1:bUO6drIvCIsvZ/XFgfxoGFQU/a4Qkh0iAlvUR7vlHJw=
+                k8s.io/klog/v2 v2.30.0/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0=
+                k8s.io/kube-openapi v0.0.0-20200805222855-6aeccd4b50c6/go.mod h1:UuqjUnNftUyPE5H64/qeyjQoUZhGpeFDVdxjTeEVN2o=
+                k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAGcJo0Tvi+dK12EcqSLqcWsryKMpfM=
+                k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw=
+                k8s.io/kube-openapi v0.0.0-20211109043538-20434351676c h1:jvamsI1tn9V0S8jicyX82qaFC0H/NKxv2e5mbqsgR80=
+                k8s.io/kube-openapi v0.0.0-20211109043538-20434351676c/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw=
+                k8s.io/kubernetes v1.13.0 h1:qTfB+u5M92k2fCCCVP2iuhgwwSOv1EkAkvQY1tQODD8=
+                k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk=
+                k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
+                k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
+                k8s.io/utils v0.0.0-20210930125809-cb0fa318a74b h1:wxEMGetGMur3J1xuGLQY7GEQYg9bZxKn3tKo5k/eYcs=
+                k8s.io/utils v0.0.0-20210930125809-cb0fa318a74b/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
+                lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
+                lukechampine.com/uint128 v1.2.0 h1:mBi/5l91vocEN8otkC5bDLhi2KdCticRiwbdB0O+rjI=
+                lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
+                modernc.org/cc/v3 v3.36.0/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI=
+                modernc.org/cc/v3 v3.36.2/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI=
+                modernc.org/cc/v3 v3.36.3 h1:uISP3F66UlixxWEcKuIWERa4TwrZENHSL8tWxZz8bHg=
+                modernc.org/cc/v3 v3.36.3/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI=
+                modernc.org/ccgo/v3 v3.0.0-20220428102840-41399a37e894/go.mod h1:eI31LL8EwEBKPpNpA4bU1/i+sKOwOrQy8D87zWUcRZc=
+                modernc.org/ccgo/v3 v3.0.0-20220430103911-bc99d88307be/go.mod h1:bwdAnOoaIt8Ax9YdWGjxWsdkPcZyRPHqrOvJxaKAKGw=
+                modernc.org/ccgo/v3 v3.16.4/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ=
+                modernc.org/ccgo/v3 v3.16.6/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ=
+                modernc.org/ccgo/v3 v3.16.8/go.mod h1:zNjwkizS+fIFDrDjIAgBSCLkWbJuHF+ar3QRn+Z9aws=
+                modernc.org/ccgo/v3 v3.16.9 h1:AXquSwg7GuMk11pIdw7fmO1Y/ybgazVkMhsZWCV0mHM=
+                modernc.org/ccgo/v3 v3.16.9/go.mod h1:zNMzC9A9xeNUepy6KuZBbugn3c0Mc9TeiJO4lgvkJDo=
+                modernc.org/ccorpus v1.11.6 h1:J16RXiiqiCgua6+ZvQot4yUuUy8zxgqbqEEUuGPlISk=
+                modernc.org/ccorpus v1.11.6/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ=
+                modernc.org/httpfs v1.0.6 h1:AAgIpFZRXuYnkjftxTAZwMIiwEqAfk8aVB2/oA6nAeM=
+                modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM=
+                modernc.org/libc v0.0.0-20220428101251-2d5f3daf273b/go.mod h1:p7Mg4+koNjc8jkqwcoFBJx7tXkpj00G77X7A72jXPXA=
+                modernc.org/libc v1.16.0/go.mod h1:N4LD6DBE9cf+Dzf9buBlzVJndKr/iJHG97vGLHYnb5A=
+                modernc.org/libc v1.16.1/go.mod h1:JjJE0eu4yeK7tab2n4S1w8tlWd9MxXLRzheaRnAKymU=
+                modernc.org/libc v1.16.17/go.mod h1:hYIV5VZczAmGZAnG15Vdngn5HSF5cSkbvfz2B7GRuVU=
+                modernc.org/libc v1.16.19/go.mod h1:p7Mg4+koNjc8jkqwcoFBJx7tXkpj00G77X7A72jXPXA=
+                modernc.org/libc v1.17.0/go.mod h1:XsgLldpP4aWlPlsjqKRdHPqCxCjISdHfM/yeWC5GyW0=
+                modernc.org/libc v1.17.1 h1:Q8/Cpi36V/QBfuQaFVeisEBs3WqoGAJprZzmf7TfEYI=
+                modernc.org/libc v1.17.1/go.mod h1:FZ23b+8LjxZs7XtFMbSzL/EhPxNbfZbErxEHc7cbD9s=
+                modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
+                modernc.org/mathutil v1.4.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
+                modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ=
+                modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
+                modernc.org/memory v1.1.1/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw=
+                modernc.org/memory v1.2.0/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw=
+                modernc.org/memory v1.2.1 h1:dkRh86wgmq/bJu2cAS2oqBCz/KsMZU7TUM4CibQ7eBs=
+                modernc.org/memory v1.2.1/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU=
+                modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
+                modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4=
+                modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
+                modernc.org/sqlite v1.18.1 h1:ko32eKt3jf7eqIkCgPAeHMBXw3riNSLhl2f3loEF7o8=
+                modernc.org/sqlite v1.18.1/go.mod h1:6ho+Gow7oX5V+OiOQ6Tr4xeqbx13UZ6t+Fw9IRUG4d4=
+                modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw=
+                modernc.org/strutil v1.1.3 h1:fNMm+oJklMGYfU9Ylcywl0CO5O6nTfaowNsh2wpPjzY=
+                modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw=
+                modernc.org/tcl v1.13.1 h1:npxzTwFTZYM8ghWicVIX1cRWzj7Nd8i6AqqX2p+IYao=
+                modernc.org/tcl v1.13.1/go.mod h1:XOLfOwzhkljL4itZkK6T72ckMgvj0BDsnKNdZVUOecw=
+                modernc.org/token v1.0.0 h1:a0jaWiNMDhDUtqOj09wvjWWAqd3q7WpBulmL9H2egsk=
+                modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
+                modernc.org/z v1.5.1 h1:RTNHdsrOpeoSeOF4FbzTo8gBYByaJ5xT7NgZ9ZqRiJM=
+                modernc.org/z v1.5.1/go.mod h1:eWFB510QWW5Th9YGZT81s+LwvaAs3Q2yr4sP0rmLkv8=
+                rsc.io/binaryregexp v0.2.0 h1:HfqmD5MEmC0zvwBuF187nq9mdnXjXsSivRiXN7SmRkE=
+                rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
+                rsc.io/pdf v0.1.1 h1:k1MczvYDUvJBe93bYd7wrZLLUEcLZAuF824/I4e5Xr4=
+                rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
+                rsc.io/quote/v3 v3.1.0 h1:9JKUTTIUgS6kzR9mK1YuGKv6Nl+DijDNIc0ghT58FaY=
+                rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
+                rsc.io/sampler v1.3.0 h1:7uVkIFmeBqHfdjD+gZwtXXI+RODJ2Wc4O7MPEh/QiW4=
+                rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
+                sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.14/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg=
+                sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.15/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg=
+                sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.22 h1:fmRfl9WJ4ApJn7LxNuED4m0t18qivVQOxP6aAYG9J6c=
+                sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.22/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg=
+                sigs.k8s.io/structured-merge-diff/v4 v4.0.1/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
+                sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
+                sigs.k8s.io/structured-merge-diff/v4 v4.0.3/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
+                sigs.k8s.io/structured-merge-diff/v4 v4.1.2 h1:Hr/htKFmJEbtMgS/UD0N+gtgctAqz81t3nu+sPzynno=
+                sigs.k8s.io/structured-merge-diff/v4 v4.1.2/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4=
+                sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
+                sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
+                sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
path: src/python/pants/backend/go/util_rules/cgo_test.py
user: login: benjyw
body: This makes this test's source code pretty unreadable and unwieldy. Maybe this should be loaded as a resource from a file? Can be in a followup though.
created_at: 2025-03-29T02:55:31Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,48 @@
+#!/usr/bin/env bash
path: .devcontainer/onCreateCommand.sh
user: login: erjac77
body: It doesn't really bother me, it's just that I've never faced this issue. I'm fine with that.
created_at: 2025-03-30T02:43:24Z
author_association: CONTRIBUTOR

PR comments:
user: login: benjyw
created_at: 2025-03-17T00:04:10Z
body: Related: https://github.com/pantsbuild/pants/pull/22096

So that changes like this don't waste time and CPU on unnecessary CI.
author_association: CONTRIBUTOR
user: login: kdowney-lot49
created_at: 2025-03-24T11:39:28Z
body: @benjyw @erjac77 merged in `base:noble` and `deadsnakes` changes and brought up to date with main. With the following modified command to get around the limits of my laptop, all but five tests pass out of the box running dev container on an Apple M1 chip with a `colima` Docker host configured for a 250 GB disk and 10 GB memory allocation, though without a lot of resources it's definitely shaky.

```
nohup ./pants test :: --test-attempts-default=3 --no-test-timeouts --test-timeout-default=10000 --test-report > pants_nohup.out 2>&1 &
```

I will work through the remaining small issues to get the five issues fixed.



author_association: CONTRIBUTOR
user: login: benjyw
created_at: 2025-03-28T22:29:21Z
body: Is this ready for another look? 
author_association: CONTRIBUTOR
user: login: kdowney-lot49
created_at: 2025-03-28T23:36:02Z
body: > Is this ready for another look?

Yes, all set.
author_association: CONTRIBUTOR