diff --git a/.devcontainer/scripts/postCreate.sh b/.devcontainer/scripts/postCreate.sh index 885487a..0c2ad59 100755 --- a/.devcontainer/scripts/postCreate.sh +++ b/.devcontainer/scripts/postCreate.sh @@ -4,7 +4,7 @@ set -euo pipefail echo "🔧 Installing devcontainer CLI..." npm install -g @devcontainers/cli -FEATURES=("ag" "amazon-q-cli" "zip") +FEATURES=("ag" "amazon-q-cli" "gcloud-cli" "zip") BASE_IMAGES=("debian:latest" "ubuntu:latest" "mcr.microsoft.com/devcontainers/base:ubuntu") # Run autogenerated tests for each image diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index fd3a190..d2aa4cb 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -15,6 +15,7 @@ jobs: features: - ag - amazon-q-cli + - gcloud-cli - zip baseImage: - debian:latest diff --git a/README.md b/README.md index abd576e..8c48bcc 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,7 @@ This repository contains following features: - [ag](./src/ag/README.md): Installs ag (The Silver Searcher), a fast grep-like text search tool - [amazon-q-cli](./src/amazon-q-cli/README.md): Install Amazon Q CLI for AWS development +- [gcloud-cli](./src/gcloud-cli/README.md): Installs Google Cloud CLI (gcloud) for Google Cloud Platform development - [zip](./src/zip/README.md): Installs zip and unzip CLI tools for compression and extraction ## Usage @@ -20,6 +21,7 @@ These examples show how to use features from this repository in a devcontainer: "features": { "ghcr.io/jajera/features/ag:1": {}, "ghcr.io/jajera/features/amazon-q-cli:1": {}, + "ghcr.io/jajera/features/gcloud-cli:1": {}, "ghcr.io/jajera/features/zip:1": {} } } @@ -31,7 +33,7 @@ Or use individual features: { "image": "mcr.microsoft.com/devcontainers/base:ubuntu", "features": { - "ghcr.io/jajera/features/ag:1": {} + "ghcr.io/jajera/features/gcloud-cli:1": {} } } ``` @@ -50,6 +52,10 @@ Similar to the [`devcontainers/features`](https://github.com/devcontainers/featu │ │ ├── devcontainer-feature.json │ │ ├── install.sh │ │ └── README.md +│ ├── gcloud-cli +│ │ ├── devcontainer-feature.json +│ │ ├── install.sh +│ │ └── README.md │ ├── zip │ │ ├── devcontainer-feature.json │ │ ├── install.sh @@ -57,6 +63,7 @@ Similar to the [`devcontainers/features`](https://github.com/devcontainers/featu ├── test │ ├── ag │ ├── amazon-q-cli +│ ├── gcloud-cli │ └── zip ... ``` diff --git a/src/gcloud-cli/README.md b/src/gcloud-cli/README.md new file mode 100644 index 0000000..07b44e9 --- /dev/null +++ b/src/gcloud-cli/README.md @@ -0,0 +1,96 @@ +# Google Cloud CLI (gcloud-cli) + +Installs `gcloud` (Google Cloud CLI), the command-line interface for Google Cloud Platform. + +## Example Usage + +```json +"features": { + "ghcr.io/jajera/features/gcloud-cli:1": {} +} +``` + +## Options + +This feature has no configurable options. + +## Description + +This feature installs `gcloud` (Google Cloud CLI), the command-line interface for Google Cloud Platform. The installation is compatible with multiple Linux distributions and package managers including: + +- **Debian/Ubuntu**: Uses `apt-get` +- **RHEL/CentOS/Fedora**: Uses `yum`/`dnf` +- **Alpine Linux**: Uses `apk` +- **Arch Linux**: Uses `pacman` +- **openSUSE**: Uses `zypper` + +## Features + +- ✅ Cross-platform support for major Linux distributions +- ✅ Automatic package manager detection +- ✅ Retry logic for package updates +- ✅ Privilege escalation handling (sudo when needed) +- ✅ Comprehensive error handling +- ✅ Installation verification +- ✅ Automatic shell completion setup (bash and zsh) +- ✅ Symlinks for gcloud, gsutil, and bq binaries +- ✅ Non-interactive initialization +- ✅ Support for x86_64 and ARM64 architectures + +### Package Names by Platform + +Dependencies installed by platform: + +- Debian/Ubuntu: `curl`, `gnupg`, `lsb-release` +- RHEL/CentOS/Fedora: `curl`, `gnupg2` +- Alpine: `curl`, `gnupg` +- Arch: `curl`, `gnupg` +- openSUSE: `curl`, `gnupg2` + +## Usage Examples + +After installation, you can use the Google Cloud CLI tools: + +```bash +# Check gcloud version +gcloud version + +# Initialize gcloud (if not done automatically) +gcloud init + +# List available projects +gcloud projects list + +# Set active project +gcloud config set project PROJECT_ID + +# List compute instances +gcloud compute instances list + +# Use gsutil for Cloud Storage operations +gsutil ls gs:// + +# Use bq for BigQuery operations +bq ls +``` + +## Verification + +The feature automatically verifies successful installation by: + +- Checking if `gcloud` command is available +- Running version check for the utility +- Verifying `gsutil` and `bq` binaries are accessible +- Testing basic gcloud functionality +- Setting up shell completion for bash and zsh + +## Shell Completion + +The feature automatically sets up shell completion: + +- **Bash**: Completion is installed to `/etc/bash_completion.d/` (system-wide) or `~/.bash_completion.d/` (user-specific) +- **Zsh**: Completion is sourced in `~/.zshrc` or `/etc/zsh/zshrc` + +--- + +_Note, this file was auto-generated from the [devcontainer-feature.json](https://github.com/jajera/features/blob/main/src/gcloud-cli/devcontainer-feature.json). Add additional notes to a `NOTES.md`._ diff --git a/src/gcloud-cli/devcontainer-feature.json b/src/gcloud-cli/devcontainer-feature.json new file mode 100644 index 0000000..0fb85aa --- /dev/null +++ b/src/gcloud-cli/devcontainer-feature.json @@ -0,0 +1,7 @@ +{ + "name": "Google Cloud CLI", + "id": "gcloud-cli", + "version": "1.0.0", + "description": "Installs Google Cloud CLI (gcloud) for Google Cloud Platform development.", + "documentationURL": "https://cloud.google.com/sdk/docs/install" +} diff --git a/src/gcloud-cli/install.sh b/src/gcloud-cli/install.sh new file mode 100755 index 0000000..044b620 --- /dev/null +++ b/src/gcloud-cli/install.sh @@ -0,0 +1,256 @@ +#!/bin/sh +set -e + +echo "Activating feature 'gcloud-cli'" + +# Detect OS and architecture +OS="$(uname -s)" +ARCH="$(uname -m)" + +if [ "$OS" != "Linux" ]; then + echo "Google Cloud CLI is currently only supported on Linux" + exit 1 +fi + +echo "✅ Detected OS: $OS" +echo "✅ Detected ARCH: $ARCH" + +# Skip install if already present +if command -v gcloud >/dev/null 2>&1; then + echo "✅ Google Cloud CLI is already installed. Skipping install." + exit 0 +fi + +echo "Installing Google Cloud CLI..." + +# Function to run package managers with appropriate privileges +run_cmd() { + if [ "$(id -u)" -eq 0 ]; then + "$@" + elif command -v sudo >/dev/null 2>&1; then + sudo "$@" + else + echo "⚠️ Warning: Running as non-root user without sudo. Package installation may fail." + "$@" + fi +} + +# Retry apt-get update up to 3 times +try_apt_update() { + for i in 1 2 3; do + echo "Running apt-get update (attempt $i)..." + if run_cmd apt-get update -y; then + return 0 + fi + echo "apt-get update failed, retrying in 2 seconds..." + sleep 2 + done + echo "ERROR: apt-get update failed after multiple attempts." + return 1 +} + +# Function to install dependencies based on the OS/package manager +install_dependencies() { + if command -v apt-get >/dev/null 2>&1; then + # Debian/Ubuntu + echo "📦 Installing dependencies via apt-get..." + if ! try_apt_update; then + echo "⚠️ Warning: Could not update package lists. Proceeding anyway..." + fi + run_cmd apt-get install -y curl gnupg lsb-release + elif command -v yum >/dev/null 2>&1; then + # RHEL/CentOS/Fedora (older) + echo "📦 Installing dependencies via yum..." + run_cmd yum install -y curl gnupg2 + elif command -v dnf >/dev/null 2>&1; then + # Fedora (newer) + echo "📦 Installing dependencies via dnf..." + run_cmd dnf install -y curl gnupg2 + elif command -v apk >/dev/null 2>&1; then + # Alpine Linux + echo "📦 Installing dependencies via apk..." + run_cmd apk add --no-cache curl gnupg + elif command -v pacman >/dev/null 2>&1; then + # Arch Linux + echo "📦 Installing dependencies via pacman..." + run_cmd pacman -S --noconfirm curl gnupg + elif command -v zypper >/dev/null 2>&1; then + # openSUSE + echo "📦 Installing dependencies via zypper..." + run_cmd zypper install -y curl gnupg2 + else + echo "❌ ERROR: No supported package manager found (apt-get, yum, dnf, apk, pacman, zypper)" + echo "Please install curl and gnupg manually" + exit 1 + fi +} + +# Install dependencies +echo "Installing required dependencies..." +if ! install_dependencies; then + echo "❌ ERROR: Could not install dependencies via package manager" + exit 1 +fi + +# Check if curl is available +if ! command -v curl >/dev/null 2>&1; then + echo "❌ ERROR: curl is required but not available" + exit 1 +fi + +# Determine architecture-specific URL +case "$ARCH" in +x86_64) + ARCH_NAME="x86_64" + ;; +aarch64) + ARCH_NAME="arm" + ;; +*) + echo "❌ Unsupported architecture: $ARCH" + exit 1 + ;; +esac + +# Set install directory based on user permissions +if [ "$(id -u)" -eq 0 ]; then + INSTALL_DIR="/usr/local/share/google-cloud-sdk" + BIN_DIR="/usr/local/bin" + BASHRC_FILE="/etc/bash.bashrc" +else + INSTALL_DIR="$HOME/.local/share/google-cloud-sdk" + BIN_DIR="$HOME/.local/bin" + BASHRC_FILE="$HOME/.bashrc" + # Ensure local bin directory exists and is in PATH + mkdir -p "$BIN_DIR" + if ! echo "$PATH" | grep -q "$HOME/.local/bin"; then + export PATH="$HOME/.local/bin:$PATH" + fi +fi + +echo "📁 Install directory: $INSTALL_DIR" + +# Download and install +TEMP_DIR=$(mktemp -d) +cd "$TEMP_DIR" + +echo "⬇️ Downloading Google Cloud CLI..." +curl --proto '=https' --tlsv1.2 -sSf "https://dl.google.com/dl/cloudsdk/channels/rapid/downloads/google-cloud-cli-linux-${ARCH_NAME}.tar.gz" -o google-cloud-cli.tar.gz + +echo "📦 Installing Google Cloud CLI..." +tar -xzf google-cloud-cli.tar.gz + +echo "Installing Google Cloud CLI to $INSTALL_DIR..." + +# Create installation directory +mkdir -p "$INSTALL_DIR" + +# Copy the SDK +cp -r google-cloud-sdk/* "$INSTALL_DIR/" + +# Set up the installation manually instead of using the problematic installer script +echo "🔧 Setting up Google Cloud CLI manually..." + +# Remove any file at $INSTALL_DIR/properties before creating the directory +if [ -f "$INSTALL_DIR/properties" ]; then + rm -f "$INSTALL_DIR/properties" +fi + +# Create the properties directory +PROPERTIES_DIR="$INSTALL_DIR/properties" +mkdir -p "$PROPERTIES_DIR" + +# Create a basic properties file +cat > "$PROPERTIES_DIR/core.json" << 'EOF' +{ + "disable_usage_reporting": true, + "disable_updater": true +} +EOF + +# Create symlinks +echo "🔗 Creating symlinks..." +if [ ! -L "$BIN_DIR/gcloud" ]; then + ln -sf "$INSTALL_DIR/bin/gcloud" "$BIN_DIR/gcloud" +fi + +if [ ! -L "$BIN_DIR/gsutil" ]; then + ln -sf "$INSTALL_DIR/bin/gsutil" "$BIN_DIR/gsutil" +fi + +if [ ! -L "$BIN_DIR/bq" ]; then + ln -sf "$INSTALL_DIR/bin/bq" "$BIN_DIR/bq" +fi + +# Make binaries executable +chmod +x "$INSTALL_DIR/bin/gcloud" +chmod +x "$INSTALL_DIR/bin/gsutil" +chmod +x "$INSTALL_DIR/bin/bq" + +# Set up shell completion if possible +if [ -f "$INSTALL_DIR/completion.bash.inc" ]; then + echo "🔧 Setting up bash completion..." + if [ "$(id -u)" -eq 0 ]; then + # For root, add to system bashrc + if ! grep -q "google-cloud-sdk/completion.bash.inc" "$BASHRC_FILE" 2>/dev/null; then + echo "" >> "$BASHRC_FILE" + echo "# Google Cloud SDK completion" >> "$BASHRC_FILE" + echo "source '$INSTALL_DIR/completion.bash.inc'" >> "$BASHRC_FILE" + fi + else + # For non-root, add to user bashrc + if ! grep -q "google-cloud-sdk/completion.bash.inc" "$BASHRC_FILE" 2>/dev/null; then + echo "" >> "$BASHRC_FILE" + echo "# Google Cloud SDK completion" >> "$BASHRC_FILE" + echo "source '$INSTALL_DIR/completion.bash.inc'" >> "$BASHRC_FILE" + fi + fi +fi + +# Set up PATH if not already set +if [ "$(id -u)" -eq 0 ]; then + # For root, create a profile.d script + PROFILE_SCRIPT="/etc/profile.d/google-cloud-sdk.sh" + cat > "$PROFILE_SCRIPT" << EOF +#!/bin/sh +export PATH="$INSTALL_DIR/bin:\$PATH" +EOF + chmod +x "$PROFILE_SCRIPT" +else + # For non-root, add to user bashrc if not already there + if ! grep -q "google-cloud-sdk/bin" "$BASHRC_FILE" 2>/dev/null; then + echo "" >> "$BASHRC_FILE" + echo "# Google Cloud SDK PATH" >> "$BASHRC_FILE" + echo "export PATH=\"$INSTALL_DIR/bin:\$PATH\"" >> "$BASHRC_FILE" + fi +fi + +# Clean up +cd / +rm -rf "$TEMP_DIR" + +# Verify installation +echo "🔍 Verifying installation..." +if command -v gcloud >/dev/null 2>&1; then + echo "✅ Google Cloud CLI installed successfully." + gcloud version +else + echo "❌ ERROR: 'gcloud' command not found in PATH" + echo "Trying to add to PATH manually..." + export PATH="$INSTALL_DIR/bin:$PATH" + if command -v gcloud >/dev/null 2>&1; then + echo "✅ Google Cloud CLI found after PATH adjustment." + gcloud version + else + echo "❌ ERROR: 'gcloud' command still not found" + exit 1 + fi +fi + +# Initialize gcloud (non-interactive) +echo "🔧 Initializing Google Cloud CLI..." +"$INSTALL_DIR/bin/gcloud" init --skip-diagnostics --quiet || { + echo "⚠️ Warning: Could not initialize gcloud automatically. You may need to run 'gcloud init' manually." +} + +echo "✅ Google Cloud CLI installation complete!" diff --git a/test/gcloud-cli/test.sh b/test/gcloud-cli/test.sh new file mode 100755 index 0000000..5c0f670 --- /dev/null +++ b/test/gcloud-cli/test.sh @@ -0,0 +1,41 @@ +#!/bin/bash + +set -e + +# Import test library bundled with the devcontainer CLI +source dev-container-features-test-lib + +# Feature-specific tests - simple existence checks +check "gcloud command exists" bash -c "command -v gcloud" +check "gsutil command exists" bash -c "command -v gsutil" +check "bq command exists" bash -c "command -v bq" + +# Test basic version commands (these are fast and don't hang) +check "gcloud version" bash -c "gcloud version --format=text" +check "gsutil version" bash -c "gsutil version" +check "bq version" bash -c "bq version" + +# Test basic functionality without hanging +check "gcloud config works" bash -c "gcloud config list --format=text" +check "gcloud auth works" bash -c "gcloud auth list --format=text" + +# Check if completion files are installed +if test -f /etc/bash_completion.d/gcloud; then + check "bash completion installed system-wide" echo "Bash completion found at /etc/bash_completion.d/gcloud" +elif test -f ~/.bash_completion.d/gcloud; then + check "bash completion installed user-specific" echo "Bash completion found at ~/.bash_completion.d/gcloud" +else + echo "⚠️ Bash completion not found at expected locations — skipping related check." +fi + +# Check if zsh completion is configured +if test -f ~/.zshrc && grep -q "source.*gcloud" ~/.zshrc; then + check "zsh completion configured" echo "Zsh completion is configured in ~/.zshrc" +elif test -f /etc/zsh/zshrc && grep -q "source.*gcloud" /etc/zsh/zshrc; then + check "zsh completion configured system-wide" echo "Zsh completion is configured in /etc/zsh/zshrc" +else + echo "⚠️ Zsh completion not found in expected locations — skipping related check." +fi + +# Report results +reportResults