From 62cafcceeed82c1619172273e88a21131031fa78 Mon Sep 17 00:00:00 2001 From: Austin Moses Date: Tue, 3 Feb 2026 13:55:30 -0700 Subject: [PATCH] Initial Doppler devcontainer feature --- .github/workflows/release.yaml | 2 +- .github/workflows/test.yaml | 15 +- .github/workflows/validate.yml | 4 +- src/doppler-cli/devcontainer-feature.json | 22 +-- src/doppler-cli/install.sh | 179 ++++++++++++++++-- test/doppler-cli/_common_latest_version.sh | 27 +++ test/doppler-cli/latest_on_alpine.sh | 3 + test/doppler-cli/latest_on_debian.sh | 3 + .../latest_on_devcontainer_base.sh | 3 + test/doppler-cli/latest_on_fedora.sh | 3 + test/doppler-cli/latest_on_ubuntu.sh | 3 + test/doppler-cli/pinned_version_on_ubuntu.sh | 10 + test/doppler-cli/scenarios.json | 32 +++- test/doppler-cli/test.sh | 13 +- 14 files changed, 273 insertions(+), 46 deletions(-) create mode 100644 test/doppler-cli/_common_latest_version.sh create mode 100644 test/doppler-cli/latest_on_alpine.sh create mode 100644 test/doppler-cli/latest_on_debian.sh create mode 100644 test/doppler-cli/latest_on_devcontainer_base.sh create mode 100644 test/doppler-cli/latest_on_fedora.sh create mode 100644 test/doppler-cli/latest_on_ubuntu.sh create mode 100644 test/doppler-cli/pinned_version_on_ubuntu.sh 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..3a4051d 100644 --- a/src/doppler-cli/devcontainer-feature.json +++ b/src/doppler-cli/devcontainer-feature.json @@ -1,20 +1,16 @@ { - "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", - "containerEnv": { - "DOPPLER_CONFIG_DIR": "/doppler" + "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')" + } }, - "mounts": [ - { - "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..1603180 100644 --- a/src/doppler-cli/install.sh +++ b/src/doppler-cli/install.sh @@ -1,24 +1,171 @@ -#!/usr/bin/env bash - +#!/bin/sh 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 gpgv..." +if ! type gpgv >/dev/null 2>&1; then + echo "Installing gpgv..." + case "$PKG_MANAGER" in + apt) install_package "$PKG_MANAGER" gpgv ;; + apk) install_package "$PKG_MANAGER" gpgv ;; + dnf|yum) install_package "$PKG_MANAGER" gnupg2 ;; + *) echo "ERROR: Cannot install gpgv"; exit 1 ;; + esac +fi + +# Ensure gpgv is in PATH (may not be after fresh install) +if ! type gpgv >/dev/null 2>&1; then + echo "gpgv not in PATH, checking common locations..." + for path in /usr/bin/gpgv /bin/gpgv /usr/local/bin/gpgv; do + if [ -x "$path" ]; then + echo "Found gpgv at $path, adding to PATH" + export PATH="$(dirname "$path"):$PATH" + break + fi + done +fi + +# Final check +if ! type gpgv >/dev/null 2>&1; then + echo "ERROR: gpgv not found after installing gnupg" + exit 1 +fi + +# Strip 'v' prefix from version if present (e.g., "v3.69.0" -> "3.69.0") +VERSION="${VERSION#v}" + +# Install bash if needed (Alpine doesn't have it, but Doppler's install script requires it) +if [ "$PKG_MANAGER" = "apk" ] && ! type bash >/dev/null 2>&1; then + echo "Installing bash (required by Doppler install script)..." + apk add --no-cache bash +fi + +# Install Doppler CLI +echo "Installing Doppler CLI (version: $VERSION)..." +if [ "$VERSION" = "latest" ]; then + # Use official install script for latest (includes signature verification) + 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 + + TEMP_DIR=$(mktemp -d) + TARBALL="doppler_${VERSION}_${OS}_${ARCH}.tar.gz" + DOWNLOAD_URL="https://github.com/DopplerHQ/cli/releases/download/${VERSION}/${TARBALL}" + SIG_URL="${DOWNLOAD_URL}.sig" + + # Download tarball + echo "Downloading from: $DOWNLOAD_URL" + curl -Ls --tlsv1.2 --proto "=https" --retry 3 -o "$TEMP_DIR/$TARBALL" "$DOWNLOAD_URL" + + # Download signature + echo "Downloading signature from: $SIG_URL" + curl -Ls --tlsv1.2 --proto "=https" --retry 3 -o "$TEMP_DIR/${TARBALL}.sig" "$SIG_URL" + + # Download public key + echo "Downloading Doppler public key..." + curl -Ls --tlsv1.2 --proto "=https" --retry 3 -o "$TEMP_DIR/doppler-public-key.gpg" "https://cli.doppler.com/keys/public" + + # Verify signature + echo "Verifying signature..." + if ! gpgv --keyring "$TEMP_DIR/doppler-public-key.gpg" "$TEMP_DIR/${TARBALL}.sig" "$TEMP_DIR/$TARBALL" 2>/dev/null; then + echo "ERROR: Signature verification failed!" + rm -rf "$TEMP_DIR" + exit 1 + fi + echo "Signature verification successful." + + # Extract and install + tar -xzf "$TEMP_DIR/$TARBALL" -C "$TEMP_DIR" + mv "$TEMP_DIR/doppler" /usr/local/bin/doppler + chmod +x /usr/local/bin/doppler -# download and execute the latest installer. -curl -Ls --tlsv1.2 --proto "=https" --retry 3 https://cli.doppler.com/install.sh | sh + rm -rf "$TEMP_DIR" +fi -# 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..340f11f 100644 --- a/test/doppler-cli/test.sh +++ b/test/doppler-cli/test.sh @@ -7,12 +7,17 @@ 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'" # 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