From d90ae8acc91dfe65f0abba8eb4c3f87a26144984 Mon Sep 17 00:00:00 2001 From: Tung Bui Date: Mon, 13 Jul 2026 08:32:14 +0700 Subject: [PATCH 1/4] feat: add Google Cloud CLI (gcloud) Fills the multi-cloud parity gap - AWS and Azure CLIs were already present, GCP wasn't. Installed via the official tarball + install.sh (non-interactive, usage-reporting/path-update/command-completion disabled) rather than apt, since Google's rapid-channel distribution isn't structured the same way azure-cli's apt repo is; the install script reuses the system Python 3.12 already on PATH rather than bundling its own runtime. - Wired into the existing version-check/version-sync automation. Unlike the other CLIs, gcloud isn't GitHub-hosted, so check_latest_version.py gets a dedicated check_gcloud_version() that reads the same rapid-channel components-2.json manifest gcloud itself uses to check for updates. - `gcloud --version` needs no auth, so it's added as a real smoke test in samples/run_sample.sh (same treatment as gh/kubectl/helm), unlike awscli/azurecli which stay documentation-only since they need live credentials to demonstrate anything. - New docs/usage/gcloudcli_usage.md follows the existing per-tool usage doc pattern. Note: this is a noticeably heavier install than the other CLIs (~88 MB compressed download) - a known, accepted tradeoff for closing the GCP gap. --- Dockerfile | 12 +++++++ README.md | 3 +- docs/usage/README.md | 4 +++ docs/usage/gcloudcli_usage.md | 53 +++++++++++++++++++++++++++++ samples/README.md | 7 ++++ samples/run_sample.sh | 6 ++++ scripts/check_latest_version.py | 12 +++++++ scripts/check_version_in_toolkit.sh | 3 ++ scripts/update_latest_version.py | 1 + toolkit_info.json | 1 + 10 files changed, 101 insertions(+), 1 deletion(-) create mode 100644 docs/usage/gcloudcli_usage.md diff --git a/Dockerfile b/Dockerfile index e6ea127..b3873b1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -122,6 +122,18 @@ RUN mkdir -p /etc/apt/keyrings && \ # environment (/mnt/repo/python_env/bin/python3), which doesn't exist here. /opt/az/bin/python3 -m pip install --no-cache-dir --break-system-packages --upgrade "cryptography==${CRYPTOGRAPHY_VERSION}" +# Install Google Cloud CLI +ARG GCLOUD_VERSION=575.0.1 +RUN mkdir -p /tmp/gcloud_env/ && \ + cd /tmp/gcloud_env/ && \ + wget "https://dl.google.com/dl/cloudsdk/channels/rapid/downloads/google-cloud-cli-${GCLOUD_VERSION}-linux-x86_64.tar.gz" && \ + tar -xzf google-cloud-cli-${GCLOUD_VERSION}-linux-x86_64.tar.gz -C /opt && \ + /opt/google-cloud-sdk/install.sh --quiet --usage-reporting=false --path-update=false --command-completion=false --rc-path=/dev/null && \ + ln -sf /opt/google-cloud-sdk/bin/gcloud /usr/local/bin/gcloud && \ + ln -sf /opt/google-cloud-sdk/bin/gsutil /usr/local/bin/gsutil && \ + ln -sf /opt/google-cloud-sdk/bin/bq /usr/local/bin/bq && \ + rm -rf /tmp/gcloud_env/ + # PowerShell Installation ARG PS_VERSION=7.6.3 ARG PS_PACKAGE=powershell_${PS_VERSION}-1.deb_amd64.deb diff --git a/README.md b/README.md index 52de51e..a2cc1a2 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ ## Key Features -- **Comprehensive Toolset**: Pre-installed with tools like Git, Python, Ansible, Terraform, kubectl, Helm, GitHub CLI, AWS CLI, Azure CLI, and more. +- **Comprehensive Toolset**: Pre-installed with tools like Git, Python, Ansible, Terraform, kubectl, Helm, GitHub CLI, AWS CLI, Azure CLI, Google Cloud CLI, and more. - **Easy Integration**: Use it directly or customize it with your preferred versions. - **Efficient Updates**: Tool versions are checked automatically and verified against the real installed binaries before every release, so published images track current, patched versions rather than drifting silently. - **Configuration Reusability**: Mounts host config folders for seamless reuse across sessions. @@ -116,6 +116,7 @@ Built on `ubuntu:24.04` base image | GitHub CLI | GH_VERSION=2.96.0 | [Check](https://github.com/cli/cli/releases) | [githubcli_usage](./docs/usage/githubcli_usage.md) | | AwsCLI | AWSCLI_VERSION=2.35.21 | [Check](https://raw.githubusercontent.com/aws/aws-cli/v2/CHANGELOG.rst) | [awscli_usage](./docs/usage/awscli_usage.md) | | AzureCLI | AZURECLI_VERSION=2.88.0 | [Check](https://learn.microsoft.com/en-us/cli/azure/release-notes-azure-cli) | [azurecli_usage](./docs/usage/azurecli_usage.md) | +| GCloudCLI | GCLOUD_VERSION=575.0.1 | [Check](https://cloud.google.com/sdk/docs/release-notes) | [gcloudcli_usage](./docs/usage/gcloudcli_usage.md) | | PowerShell | PS_VERSION=7.6.3 | [Check](https://github.com/PowerShell/PowerShell/releases) | [powershell_usage](./docs/usage/powershell_usage.md) | And more tools to be implemented... diff --git a/docs/usage/README.md b/docs/usage/README.md index 8f88516..74fab4d 100644 --- a/docs/usage/README.md +++ b/docs/usage/README.md @@ -34,6 +34,10 @@ This guide us how to use the `devops-toolkit` with serveral usecases - Check [**azurecli_usage**](./azurecli_usage.md) +## GCloudCLI + +- Check [**gcloudcli_usage**](./gcloudcli_usage.md) + ## PowerShell - Check [**powershell_usage**](./powershell_usage.md) diff --git a/docs/usage/gcloudcli_usage.md b/docs/usage/gcloudcli_usage.md new file mode 100644 index 0000000..c223e25 --- /dev/null +++ b/docs/usage/gcloudcli_usage.md @@ -0,0 +1,53 @@ +# Use gcloudcli in the devops-toolkit + +## Prerequisite + +A Google Cloud account + +## gcloudcli document + +Some document to help you start with the Google Cloud CLI + +- + +## Run with Docker command + +### Note + +To use the existing container instead of creating one, use `docker exec` command instead of `docker run` + +```bash +docker exec -it my_devops_toolkit /bin/bash +``` + +### Common Run Modes + +For instructions on common run modes, visit [**DevOps Toolkit Common Run Mode**](../usage/run_mode.md). + +### Use case 1: gcloud login and run command + +```bash +docker run --rm -it -v ~/.dtc:/dtc tungbq/devops-toolkit:latest + +# Login with gcloud CLI +gcloud auth login --no-launch-browser +### Copy the printed URL into a browser, sign in, then paste the verification code back into the terminal + +# Set the active project +gcloud config set project + +# List Compute Engine instances +gcloud compute instances list +``` + +Sample Result + +```bash +root@f097467db632:~# gcloud projects list +PROJECT_ID NAME PROJECT_NUMBER +your-project-id your-project 123456789012 +``` + +### Troubleshooting + +- For any issues, check [this reference](../troubleshooting/TROUBLESHOOTING.md) diff --git a/samples/README.md b/samples/README.md index 2a14444..a189af0 100644 --- a/samples/README.md +++ b/samples/README.md @@ -70,3 +70,10 @@ gh --version ## AzureCLI - TODO + +## GCloudCLI + +```bash +# Check Google Cloud CLI version +gcloud --version +``` diff --git a/samples/run_sample.sh b/samples/run_sample.sh index 1dc0d16..e573054 100644 --- a/samples/run_sample.sh +++ b/samples/run_sample.sh @@ -51,3 +51,9 @@ helm search repo bitnami | grep jenkins console_log "Running GitHub CLI sample: check version" gh --version +########### +# Google Cloud CLI +########### +console_log "Running Google Cloud CLI sample: check version" +gcloud --version + diff --git a/scripts/check_latest_version.py b/scripts/check_latest_version.py index 0b71c1c..908a494 100644 --- a/scripts/check_latest_version.py +++ b/scripts/check_latest_version.py @@ -52,6 +52,14 @@ def check_awscli_version(self): version_pattern = re.compile(r'\b(\d+\.\d+\.\d+)\b') return self.check_version("awscli", "https://raw.githubusercontent.com/aws/aws-cli/v2/CHANGELOG.rst", version_pattern) + def check_gcloud_version(self): + # Google Cloud CLI isn't distributed via GitHub releases; its rapid-channel + # manifest is the canonical source gcloud itself uses to check for updates. + req = self.do_http_request("https://dl.google.com/dl/cloudsdk/channels/rapid/components-2.json") + latest_version = req["version"] + print(f"Latest version of Google Cloud CLI: {latest_version}") + return latest_version + def check_github_release_version(self, name, repo, tag_prefix="v"): print(f"Checking {name} version from repo {repo} using tag prefix: {tag_prefix}") latest_version = None @@ -99,6 +107,10 @@ def main(output_file): azcli_version = version_parser.check_github_release_version("Azure CLI", "Azure/azure-cli", "azure-cli-") versions["azurecli"] = azcli_version + # Google Cloud CLI + gcloud_version = version_parser.check_gcloud_version() + versions["gcloudcli"] = gcloud_version + # PowerShell Core pwsh_version = version_parser.check_github_release_version("PowerShell", "PowerShell/PowerShell", "v") versions["pwsh"] = pwsh_version diff --git a/scripts/check_version_in_toolkit.sh b/scripts/check_version_in_toolkit.sh index f9b77ef..c539c2b 100644 --- a/scripts/check_version_in_toolkit.sh +++ b/scripts/check_version_in_toolkit.sh @@ -12,6 +12,7 @@ helm_version=$(cat "$toolkit_info_path" | jq -r '.helm') githubcli_version=$(cat "$toolkit_info_path" | jq -r '.githubcli') awscli_version=$(cat "$toolkit_info_path" | jq -r '.awscli') azurecli_version=$(cat "$toolkit_info_path" | jq -r '.azurecli') +gcloudcli_version=$(cat "$toolkit_info_path" | jq -r '.gcloudcli') pwsh_version=$(cat "$toolkit_info_path" | jq -r '.pwsh') # Print the versions @@ -24,6 +25,7 @@ echo "helm_version=$helm_version" echo "githubcli_version=$githubcli_version" echo "awscli_version=$awscli_version" echo "azurecli_version=$azurecli_version" +echo "gcloudcli_version=$gcloudcli_version" echo "pwsh_version=$pwsh_version" # Define the tools and their expected versions @@ -36,6 +38,7 @@ declare -A tools=( [gh]="$githubcli_version" [aws]="$awscli_version" [az]="$azurecli_version" + [gcloud]="$gcloudcli_version" [pwsh]="$pwsh_version" # Add more tools as needed ) diff --git a/scripts/update_latest_version.py b/scripts/update_latest_version.py index 02b91d3..6d66d90 100644 --- a/scripts/update_latest_version.py +++ b/scripts/update_latest_version.py @@ -61,6 +61,7 @@ def update_readme(readme_path, version_arg, new_version): {"name": "githubcli", "version_arg": "GH_VERSION"}, {"name": "awscli", "version_arg": "AWSCLI_VERSION"}, {"name": "azurecli", "version_arg": "AZURECLI_VERSION"}, + {"name": "gcloudcli", "version_arg": "GCLOUD_VERSION"}, {"name": "pwsh", "version_arg": "PS_VERSION"} ] diff --git a/toolkit_info.json b/toolkit_info.json index a7c5faa..2603cf8 100644 --- a/toolkit_info.json +++ b/toolkit_info.json @@ -7,5 +7,6 @@ "githubcli": "2.96.0", "awscli": "2.35.21", "azurecli": "2.88.0", + "gcloudcli": "575.0.1", "pwsh": "7.6.3" } From af2feecb3bfbbf217cf4d094f5821b2743b3a927 Mon Sep 17 00:00:00 2001 From: Tung Bui Date: Mon, 13 Jul 2026 08:40:22 +0700 Subject: [PATCH 2/4] fix: resolve Trivy findings in the gcloud install Same shape of issue as azure-cli's /opt/az venv, plus a new one from gsutil's vendored dependencies: - gcloud bundles its own Python under platform/bundledpythonunix with its own pinned cryptography==46.0.7 (vulnerable OpenSSL, GHSA-537c-gmf6-5ccf), separate from the system pip environment already patched. Discover the bundled interpreter with `find` rather than hardcoding its exact filename, since the bundled-python archive layout isn't something we control. - gsutil vendors urllib3's entire source tree, including urllib3's own test-only dummy TLS server fixtures under third_party/urllib3/dummyserver/certs/. Those are checked-in dummy RSA private keys used only by urllib3's own test suite - Trivy's secret scanner (which runs independent of the vuln-type filter) flagged them as real private keys. They're dead weight in this image regardless, so delete the whole dummyserver directory instead of just suppressing the alert. --- Dockerfile | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Dockerfile b/Dockerfile index b3873b1..64bcc28 100644 --- a/Dockerfile +++ b/Dockerfile @@ -132,6 +132,16 @@ RUN mkdir -p /tmp/gcloud_env/ && \ ln -sf /opt/google-cloud-sdk/bin/gcloud /usr/local/bin/gcloud && \ ln -sf /opt/google-cloud-sdk/bin/gsutil /usr/local/bin/gsutil && \ ln -sf /opt/google-cloud-sdk/bin/bq /usr/local/bin/bq && \ + # gcloud vendors its own bundled Python under platform/bundledpythonunix + # with its own pinned deps, separate from the system pip environment - + # patch its cryptography too (same issue as azure-cli's /opt/az venv). + BUNDLED_PY=$(find /opt/google-cloud-sdk/platform/bundledpythonunix -maxdepth 2 -type f -name "python3*" ! -name "*-config" | head -1) && \ + "$BUNDLED_PY" -m pip install --no-cache-dir --break-system-packages --upgrade "cryptography==${CRYPTOGRAPHY_VERSION}" && \ + # gsutil vendors urllib3's entire source tree, including urllib3's own + # test-only dummy TLS server fixtures (dead weight here, and their + # checked-in dummy private keys trip vulnerability scanners as if they + # were real secrets) + rm -rf /opt/google-cloud-sdk/platform/gsutil/third_party/urllib3/dummyserver && \ rm -rf /tmp/gcloud_env/ # PowerShell Installation From f3a83750b45436eb0564021e2e8a1c8d5a8546c1 Mon Sep 17 00:00:00 2001 From: Tung Bui Date: Mon, 13 Jul 2026 08:46:14 +0700 Subject: [PATCH 3/4] fix: install cryptography with --no-deps in gcloud's bundled Python The previous fix used --upgrade, which let pip cascade-upgrade other packages (most likely cffi) in gcloud's bundled Python to satisfy cryptography's declared constraints. That broke the interpreter itself: gcloud errored with "problems with your Python interpreter" on every invocation, failing the version-verify CI step. gcloud's bundled Python is a tightly pinned snapshot, not a general venv - installing cryptography with --no-deps --force-reinstall touches only that one package's files and leaves everything else in the snapshot alone. --- Dockerfile | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 64bcc28..33274f8 100644 --- a/Dockerfile +++ b/Dockerfile @@ -135,8 +135,13 @@ RUN mkdir -p /tmp/gcloud_env/ && \ # gcloud vendors its own bundled Python under platform/bundledpythonunix # with its own pinned deps, separate from the system pip environment - # patch its cryptography too (same issue as azure-cli's /opt/az venv). + # --no-deps: gcloud's bundled Python is a tightly pinned snapshot: a + # plain --upgrade let pip cascade-upgrade other packages (e.g. cffi) to + # satisfy cryptography's declared constraints, which broke the + # interpreter gcloud itself runs on. Installing cryptography alone, + # with no dependency resolution, avoids touching anything else. BUNDLED_PY=$(find /opt/google-cloud-sdk/platform/bundledpythonunix -maxdepth 2 -type f -name "python3*" ! -name "*-config" | head -1) && \ - "$BUNDLED_PY" -m pip install --no-cache-dir --break-system-packages --upgrade "cryptography==${CRYPTOGRAPHY_VERSION}" && \ + "$BUNDLED_PY" -m pip install --no-cache-dir --break-system-packages --force-reinstall --no-deps "cryptography==${CRYPTOGRAPHY_VERSION}" && \ # gsutil vendors urllib3's entire source tree, including urllib3's own # test-only dummy TLS server fixtures (dead weight here, and their # checked-in dummy private keys trip vulnerability scanners as if they From 4859bd64002475dd918dfff525ce266dacf3fe25 Mon Sep 17 00:00:00 2001 From: Tung Bui Date: Mon, 13 Jul 2026 08:53:24 +0700 Subject: [PATCH 4/4] fix: bump pyOpenSSL alongside cryptography in gcloud's bundled Python Neither of the previous two attempts touched pyOpenSSL, since it's a *dependent* of cryptography, not a dependency - pip has no reason to touch it when only cryptography is named on the install line, --no-deps or not. gcloud vendors pyOpenSSL too, and it imports cryptography's internal OpenSSL binding attributes directly, so a cryptography-only bump left it calling attributes that no longer exist ("AttributeError: module 'lib' has no attribute 'GEN_EMAIL'"), breaking gcloud's own bootstrap (gcloud_main imports oauth2client -> pyOpenSSL transitively). pyOpenSSL 26.3.0 declares cryptography<50,>=49.0.0 as its constraint, which the already-pinned cryptography==49.0.0 satisfies - installing both together, still with --no-deps, keeps the rest of gcloud's pinned snapshot untouched. --- Dockerfile | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/Dockerfile b/Dockerfile index 33274f8..cabce19 100644 --- a/Dockerfile +++ b/Dockerfile @@ -135,13 +135,17 @@ RUN mkdir -p /tmp/gcloud_env/ && \ # gcloud vendors its own bundled Python under platform/bundledpythonunix # with its own pinned deps, separate from the system pip environment - # patch its cryptography too (same issue as azure-cli's /opt/az venv). - # --no-deps: gcloud's bundled Python is a tightly pinned snapshot: a - # plain --upgrade let pip cascade-upgrade other packages (e.g. cffi) to - # satisfy cryptography's declared constraints, which broke the - # interpreter gcloud itself runs on. Installing cryptography alone, - # with no dependency resolution, avoids touching anything else. + # gcloud also vendors pyOpenSSL, which imports cryptography's internal + # OpenSSL bindings directly - bumping cryptography alone (even with + # --no-deps, even with full --upgrade resolution, since pyOpenSSL isn't + # a *dependency* of cryptography so neither approach touches it) leaves + # pyOpenSSL calling attributes cryptography's bindings no longer expose + # ("AttributeError: module 'lib' has no attribute 'GEN_EMAIL'"). Bump + # both together, pinned to versions PyPI declares compatible with each + # other, and use --no-deps so nothing else in the pinned snapshot moves. BUNDLED_PY=$(find /opt/google-cloud-sdk/platform/bundledpythonunix -maxdepth 2 -type f -name "python3*" ! -name "*-config" | head -1) && \ - "$BUNDLED_PY" -m pip install --no-cache-dir --break-system-packages --force-reinstall --no-deps "cryptography==${CRYPTOGRAPHY_VERSION}" && \ + "$BUNDLED_PY" -m pip install --no-cache-dir --break-system-packages --force-reinstall --no-deps \ + "cryptography==${CRYPTOGRAPHY_VERSION}" "pyOpenSSL==26.3.0" && \ # gsutil vendors urllib3's entire source tree, including urllib3's own # test-only dummy TLS server fixtures (dead weight here, and their # checked-in dummy private keys trip vulnerability scanners as if they