diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 935ef81..22509b2 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -11,7 +11,7 @@ jobs: pull-requests: write packages: write steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: "Publish Features" uses: devcontainers/action@v1 diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 9e5f9bf..c5b2576 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -13,14 +13,16 @@ jobs: strategy: matrix: features: - - color - - hello + - doppler-cli baseImage: - debian:latest - ubuntu:latest + - alpine:latest + - fedora:latest - mcr.microsoft.com/devcontainers/base:ubuntu + - mcr.microsoft.com/devcontainers/base:debian steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: "Install latest devcontainer CLI" run: npm install -g @devcontainers/cli @@ -34,10 +36,9 @@ jobs: strategy: matrix: features: - - color - - hello + - doppler-cli steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: "Install latest devcontainer CLI" run: npm install -g @devcontainers/cli @@ -49,7 +50,7 @@ jobs: runs-on: ubuntu-latest continue-on-error: true steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: "Install latest devcontainer CLI" run: npm install -g @devcontainers/cli diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index 5dcc21b..e517a86 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -1,13 +1,13 @@ name: "Validate devcontainer-feature.json files" on: - workflow_dispatch: pull_request: + workflow_dispatch: jobs: validate: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: "Validate devcontainer-feature.json files" uses: devcontainers/action@v1 diff --git a/src/doppler-cli/devcontainer-feature.json b/src/doppler-cli/devcontainer-feature.json index 95909ab..7c8a6da 100644 --- a/src/doppler-cli/devcontainer-feature.json +++ b/src/doppler-cli/devcontainer-feature.json @@ -1,20 +1,26 @@ { - "name": "Doppler CLI", "id": "doppler-cli", "version": "1.0.0", - "description": "A feature that installs the Doppler CLI", - "documentationURL": "https://github.com/dopplerhq/devcontainer-features/tree/main/src/doppler-cli", + "name": "Doppler CLI", + "description": "Installs the Doppler CLI for secrets management.", + "options": { + "version": { + "type": "string", + "default": "latest", + "description": "Version of the Doppler CLI to install (e.g., '3.67.0' or 'latest')" + } + }, "containerEnv": { "DOPPLER_CONFIG_DIR": "/doppler" }, "mounts": [ - { - "source": "doppler-cli-user-config", - "target": "/doppler", - "type": "volume" - } + { + "source": "doppler-cli-user-config", + "target": "/doppler", + "type": "volume" + } ], "installsAfter": [ - "ghcr.io/devcontainers/features/common-utils" + "ghcr.io/devcontainers/features/common-utils" ] } diff --git a/src/doppler-cli/install.sh b/src/doppler-cli/install.sh index 4a581f6..b6ddd85 100644 --- a/src/doppler-cli/install.sh +++ b/src/doppler-cli/install.sh @@ -1,24 +1,158 @@ #!/usr/bin/env bash - set -e -ensure_curl() { - if ! type curl >/dev/null 2>&1; then - apt-get update -y && apt-get -y install --no-install-recommends curl - fi +echo "=== Doppler CLI Feature Install ===" +echo "VERSION: ${VERSION:-latest}" +echo "_REMOTE_USER: ${_REMOTE_USER:-not set}" + +VERSION="${VERSION:-latest}" + +# Detect package manager +detect_package_manager() { + if type apt-get >/dev/null 2>&1; then + echo "apt" + elif type apk >/dev/null 2>&1; then + echo "apk" + elif type dnf >/dev/null 2>&1; then + echo "dnf" + elif type yum >/dev/null 2>&1; then + echo "yum" + else + echo "unknown" + fi } -ensure_gpg() { - if ! type gpg >/dev/null 2>&1; then - apt-get update -y && apt-get -y install --no-install-recommends gnupg2 - fi +# Install a package using the detected package manager +install_package() { + local pkg_manager="$1" + shift + local packages="$@" + + case "$pkg_manager" in + apt) + apt-get update -y && apt-get install -y --no-install-recommends $packages + rm -rf /var/lib/apt/lists/* + ;; + apk) + apk add --no-cache $packages + ;; + dnf) + dnf install -y $packages && dnf clean all + rm -rf /var/cache/dnf + ;; + yum) + yum install -y $packages && yum clean all + rm -rf /var/cache/yum + ;; + *) + echo "ERROR: Unsupported package manager. Please install manually: $packages" + exit 1 + ;; + esac } -ensure_curl -ensure_gpg +PKG_MANAGER=$(detect_package_manager) +echo "Detected package manager: $PKG_MANAGER" + +# Install dependencies if missing +echo "Checking for curl..." +if ! type curl >/dev/null 2>&1; then + echo "Installing curl..." + case "$PKG_MANAGER" in + apt) install_package "$PKG_MANAGER" curl ca-certificates ;; + apk) install_package "$PKG_MANAGER" curl ca-certificates ;; + dnf|yum) install_package "$PKG_MANAGER" curl ;; + *) echo "ERROR: Cannot install curl"; exit 1 ;; + esac +fi + +echo "Checking for gpg..." +if ! type gpg >/dev/null 2>&1; then + echo "Installing gnupg..." + case "$PKG_MANAGER" in + apt) install_package "$PKG_MANAGER" gnupg ;; + apk) install_package "$PKG_MANAGER" gnupg ;; + dnf|yum) install_package "$PKG_MANAGER" gnupg2 ;; + *) echo "ERROR: Cannot install gnupg"; exit 1 ;; + esac +fi + +# Strip 'v' prefix from version if present (e.g., "v3.69.0" -> "3.69.0") +VERSION="${VERSION#v}" + +# Install Doppler CLI +echo "Installing Doppler CLI (version: $VERSION)..." +if [ "$VERSION" = "latest" ]; then + # Use official install script for latest + echo "Fetching Doppler install script..." + curl -Ls --tlsv1.2 --proto "=https" --retry 3 https://cli.doppler.com/install.sh | bash +else + # Download specific version directly from GitHub releases + echo "Downloading Doppler CLI v$VERSION from GitHub releases..." + + # Detect OS + OS=$(uname -s | tr '[:upper:]' '[:lower:]') + case "$OS" in + darwin) OS="macos" ;; + linux) OS="linux" ;; + *) echo "ERROR: Unsupported OS: $OS"; exit 1 ;; + esac + + # Detect architecture + ARCH=$(uname -m) + case "$ARCH" in + x86_64|amd64) ARCH="amd64" ;; + aarch64|arm64) ARCH="arm64" ;; + armv7l) ARCH="armv7" ;; + armv6l) ARCH="armv6" ;; + i386|i686) ARCH="i386" ;; + *) echo "ERROR: Unsupported architecture: $ARCH"; exit 1 ;; + esac + + # Download and extract + DOWNLOAD_URL="https://github.com/DopplerHQ/cli/releases/download/${VERSION}/doppler_${VERSION}_${OS}_${ARCH}.tar.gz" + echo "Downloading from: $DOWNLOAD_URL" + + TEMP_DIR=$(mktemp -d) + curl -Ls --tlsv1.2 --proto "=https" --retry 3 -o "$TEMP_DIR/doppler.tar.gz" "$DOWNLOAD_URL" + + tar -xzf "$TEMP_DIR/doppler.tar.gz" -C "$TEMP_DIR" + mv "$TEMP_DIR/doppler" /usr/local/bin/doppler + chmod +x /usr/local/bin/doppler + + rm -rf "$TEMP_DIR" +fi + +# Ensure config directory is writable by the container user +# Use smart detection if _REMOTE_USER is not set +echo "Creating config directory /doppler..." +mkdir -p /doppler + +# Determine user for config directory ownership +if [ -z "${_REMOTE_USER}" ] || [ "${_REMOTE_USER}" = "root" ]; then + # Try common dev container users first + for user in vscode node codespace; do + if id -u "$user" > /dev/null 2>&1; then + _REMOTE_USER="$user" + echo "Detected dev container user: $user" + break + fi + done +fi + +# If still not found, try UID 1000 (common default for container users) +if [ -z "${_REMOTE_USER}" ] || [ "${_REMOTE_USER}" = "root" ]; then + _REMOTE_USER="$(awk -v val=1000 -F ':' '$3==val{print $1}' /etc/passwd)" + if [ -n "${_REMOTE_USER}" ]; then + echo "Detected user with UID 1000: $_REMOTE_USER" + fi +fi + +# Finally, fallback to root +_REMOTE_USER="${_REMOTE_USER:-root}" -# download and execute the latest installer. -curl -Ls --tlsv1.2 --proto "=https" --retry 3 https://cli.doppler.com/install.sh | sh +echo "Setting ownership to ${_REMOTE_USER}..." +chown -R "${_REMOTE_USER}:${_REMOTE_USER}" /doppler -# ensure /doppler is writable to store CLI config data -mkdir -p /doppler && chown -R ${_REMOTE_USER}:${_REMOTE_USER} /doppler +echo "=== Installation complete ===" +echo "Doppler CLI installed: $(doppler --version)" diff --git a/test/doppler-cli/_common_latest_version.sh b/test/doppler-cli/_common_latest_version.sh new file mode 100644 index 0000000..6384bce --- /dev/null +++ b/test/doppler-cli/_common_latest_version.sh @@ -0,0 +1,27 @@ +#!/usr/bin/env bash +# Shared test logic for verifying latest version is installed + +set -e + +source dev-container-features-test-lib + +# Install jq if needed for JSON parsing +if ! type jq >/dev/null 2>&1; then + if type apt-get >/dev/null 2>&1; then + apt-get update -y && apt-get -y install --no-install-recommends jq + elif type apk >/dev/null 2>&1; then + apk add --no-cache jq + elif type dnf >/dev/null 2>&1; then + dnf install -y jq + elif type yum >/dev/null 2>&1; then + yum install -y jq + fi +fi + +# Get latest version from GitHub API +latest_version=$(curl -s https://api.github.com/repos/dopplerhq/cli/releases/latest | jq -r '.tag_name') + +# Verify installed version matches latest +check "latest version installed" bash -c "doppler -v | grep '${latest_version}'" + +reportResults diff --git a/test/doppler-cli/latest_on_alpine.sh b/test/doppler-cli/latest_on_alpine.sh new file mode 100644 index 0000000..6d37a8a --- /dev/null +++ b/test/doppler-cli/latest_on_alpine.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env bash +# Test latest version on Alpine +source "$(dirname "$0")/_common_latest_version.sh" diff --git a/test/doppler-cli/latest_on_debian.sh b/test/doppler-cli/latest_on_debian.sh new file mode 100644 index 0000000..0ab5586 --- /dev/null +++ b/test/doppler-cli/latest_on_debian.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env bash +# Test latest version on Debian +source "$(dirname "$0")/_common_latest_version.sh" diff --git a/test/doppler-cli/latest_on_devcontainer_base.sh b/test/doppler-cli/latest_on_devcontainer_base.sh new file mode 100644 index 0000000..d210cc1 --- /dev/null +++ b/test/doppler-cli/latest_on_devcontainer_base.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env bash +# Test latest version on Microsoft devcontainer base +source "$(dirname "$0")/_common_latest_version.sh" \ No newline at end of file diff --git a/test/doppler-cli/latest_on_fedora.sh b/test/doppler-cli/latest_on_fedora.sh new file mode 100644 index 0000000..b866b2f --- /dev/null +++ b/test/doppler-cli/latest_on_fedora.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env bash +# Test latest version on Fedora +source "$(dirname "$0")/_common_latest_version.sh" diff --git a/test/doppler-cli/latest_on_ubuntu.sh b/test/doppler-cli/latest_on_ubuntu.sh new file mode 100644 index 0000000..1c242ac --- /dev/null +++ b/test/doppler-cli/latest_on_ubuntu.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env bash +# Test latest version on Ubuntu +source "$(dirname "$0")/_common_latest_version.sh" \ No newline at end of file diff --git a/test/doppler-cli/pinned_version_on_ubuntu.sh b/test/doppler-cli/pinned_version_on_ubuntu.sh new file mode 100644 index 0000000..9763fe8 --- /dev/null +++ b/test/doppler-cli/pinned_version_on_ubuntu.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash + +set -e + +source dev-container-features-test-lib + +# Verify the specific pinned version is installed +check "pinned version 3.69.0 installed" bash -c "doppler -v | grep 'v3.69.0'" + +reportResults diff --git a/test/doppler-cli/scenarios.json b/test/doppler-cli/scenarios.json index 9cea1ce..36426bc 100644 --- a/test/doppler-cli/scenarios.json +++ b/test/doppler-cli/scenarios.json @@ -1,14 +1,40 @@ { - "doppler_cli_installed": { - "image": "mcr.microsoft.com/devcontainers/base:ubuntu", + "latest_on_ubuntu": { + "image": "ubuntu:latest", + "features": { + "doppler-cli": {} + } + }, + "latest_on_debian": { + "image": "debian:latest", + "features": { + "doppler-cli": {} + } + }, + "latest_on_alpine": { + "image": "alpine:latest", + "features": { + "doppler-cli": {} + } + }, + "latest_on_fedora": { + "image": "fedora:latest", "features": { "doppler-cli": {} } }, - "doppler_cli_version": { + "latest_on_devcontainer_base": { "image": "mcr.microsoft.com/devcontainers/base:ubuntu", "features": { "doppler-cli": {} } + }, + "pinned_version_on_ubuntu": { + "image": "ubuntu:latest", + "features": { + "doppler-cli": { + "version": "3.69.0" + } + } } } \ No newline at end of file diff --git a/test/doppler-cli/test.sh b/test/doppler-cli/test.sh index 402b5e1..97bc818 100644 --- a/test/doppler-cli/test.sh +++ b/test/doppler-cli/test.sh @@ -7,12 +7,24 @@ set -e -# Optional: Import test library bundled with the devcontainer CLI -# Provides the 'check' and 'reportResults' commands. +# Import test library bundled with the devcontainer CLI source dev-container-features-test-lib -check "version" doppler -v +# Check CLI is installed and executable +check "doppler is installed" command -v doppler + +# Check version outputs valid semver format +check "version format" bash -c "doppler -v | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+'" + +# Check CLI responds to help +check "help output" bash -c "doppler -h | grep 'The official Doppler CLI'" + +# Check config directory exists and is writable +check "config directory exists" test -d /doppler +check "config directory writable" test -w /doppler + +# Check DOPPLER_CONFIG_DIR is set +check "DOPPLER_CONFIG_DIR set" test "$DOPPLER_CONFIG_DIR" = "/doppler" # Report results -# If any of the checks above exited with a non-zero exit code, the test will fail. reportResults \ No newline at end of file