name: Test Installation on: push: tags: # Temporary compatibility window: support both legacy v* and package-scoped CLI tags. - 'v*' - '@composio/cli@*' workflow_dispatch: inputs: version: description: 'Version to test (e.g., 1.0.0 or @composio/cli@1.0.0)' required: true workflow_call: inputs: version: description: 'Release tag or semver version to test' required: false default: 'latest' type: string permissions: contents: read jobs: toolchain-versions: name: Read toolchain test matrix runs-on: ubuntu-latest outputs: node_install_compat: ${{ steps.versions.outputs.node_install_compat }} steps: - name: Checkout uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: Read versions id: versions run: echo "node_install_compat=$(jq -c '.node_install_compat' toolchain-versions.json)" >> "$GITHUB_OUTPUT" install-script-unit-tests: name: Install Script Unit Tests runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: Test release resolution fallback run: bash test/install-sh-release-resolution.test.sh test-install-script: name: Test Install Script needs: install-script-unit-tests runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: include: # Ubuntu x64 versions - os: ubuntu-22.04 name: 'Ubuntu 22.04 x64' shell: bash - os: ubuntu-22.04 name: 'Ubuntu 22.04 x64 (zsh)' shell: zsh - os: ubuntu-latest name: 'Ubuntu Latest x64' shell: bash # Ubuntu ARM64 versions (using Depot) - os: depot-ubuntu-24.04-arm name: 'Ubuntu 24.04 ARM64' shell: bash - os: depot-ubuntu-22.04-arm name: 'Ubuntu 22.04 ARM64' shell: bash - os: depot-ubuntu-22.04-arm name: 'Ubuntu 22.04 ARM64 (zsh)' shell: zsh # macOS versions - os: macos-15-intel name: 'macOS 15 (Intel)' shell: bash - os: macos-15-intel name: 'macOS 15 (Intel, zsh)' shell: zsh - os: macos-14 name: 'macOS 14 (Sonoma)' shell: bash - os: macos-15 name: 'macOS 15 (Apple Silicon)' shell: bash - os: macos-15 name: 'macOS 15 (Apple Silicon, zsh)' shell: zsh steps: - name: Checkout uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: Resolve and validate target release id: release_target shell: bash env: VERSION_INPUT: ${{ inputs.version }} run: | raw_input="$VERSION_INPUT" if [[ -z "$raw_input" || "$raw_input" == "latest" ]]; then echo "use_latest=true" >> "$GITHUB_OUTPUT" echo "release_tag=" >> "$GITHUB_OUTPUT" exit 0 fi # For manual runs, accept a full package tag or semver and normalize to a package tag. if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then if [[ "$raw_input" =~ ^@composio/cli@ ]]; then echo "use_latest=false" >> "$GITHUB_OUTPUT" echo "release_tag=$raw_input" >> "$GITHUB_OUTPUT" exit 0 elif [[ "$raw_input" =~ ^[0-9]+\.[0-9]+\.[0-9]+([-.][0-9A-Za-z.-]+)?(\+[0-9A-Za-z.-]+)?$ ]]; then echo "use_latest=false" >> "$GITHUB_OUTPUT" echo "release_tag=@composio/cli@$raw_input" >> "$GITHUB_OUTPUT" exit 0 fi echo "Invalid version: '$raw_input'" echo "Expected format like: 1.2.3, 1.2.3-beta.1, 1.2.3+build.7, or @composio/cli@1.2.3" exit 1 fi # For workflow_call / push, accept a full package tag, semver, or legacy v* tag. if [[ "$raw_input" =~ ^@composio/cli@ ]]; then echo "use_latest=false" >> "$GITHUB_OUTPUT" echo "release_tag=$raw_input" >> "$GITHUB_OUTPUT" elif [[ "$raw_input" =~ ^[0-9]+\.[0-9]+\.[0-9]+([-.][0-9A-Za-z.-]+)?(\+[0-9A-Za-z.-]+)?$ ]]; then echo "use_latest=false" >> "$GITHUB_OUTPUT" echo "release_tag=@composio/cli@$raw_input" >> "$GITHUB_OUTPUT" else echo "use_latest=false" >> "$GITHUB_OUTPUT" echo "release_tag=$raw_input" >> "$GITHUB_OUTPUT" exit 0 fi - name: Setup shell environment run: | if [[ "${{ matrix.shell }}" == "zsh" ]]; then # Install zsh if not present if ! command -v zsh &> /dev/null; then if [[ "${{ runner.os }}" == "Linux" ]]; then sudo apt-get update sudo apt-get install -y zsh elif [[ "${{ runner.os }}" == "macOS" ]]; then # zsh is default on macOS echo "zsh already available" fi fi # Create .zshrc if it doesn't exist touch ~/.zshrc # Set zsh as the shell for this session export SHELL=$(which zsh) echo "SHELL=$(which zsh)" >> $GITHUB_ENV else # Ensure bash is available and create .bashrc touch ~/.bashrc export SHELL=$(which bash) echo "SHELL=$(which bash)" >> $GITHUB_ENV fi - name: Test install script (latest version) if: steps.release_target.outputs.use_latest == 'true' shell: bash run: | echo "Testing installation with auto-detected latest CLI version..." # Test the install script without version (it will auto-detect latest CLI release) bash install.sh # Verify installation source ~/.bashrc 2>/dev/null || source ~/.zshrc 2>/dev/null || true export PATH="$HOME/.composio:$PATH" # Check if binary exists if [[ -f "$HOME/.composio/composio" ]]; then echo "✅ Binary installed successfully" ls -la "$HOME/.composio/composio" else echo "❌ Binary not found" exit 1 fi # Test binary execution if "$HOME/.composio/composio" --version; then echo "✅ Binary executes successfully" else echo "❌ Binary execution failed" exit 1 fi - name: Test install script (specific version) if: steps.release_target.outputs.use_latest != 'true' shell: bash env: RELEASE_TAG: ${{ steps.release_target.outputs.release_tag }} run: | echo "Testing installation with version: ${RELEASE_TAG}" # Test the install script with specific version bash install.sh "${RELEASE_TAG}" # Verify installation source ~/.bashrc 2>/dev/null || source ~/.zshrc 2>/dev/null || true export PATH="$HOME/.composio:$PATH" # Check if binary exists if [[ -f "$HOME/.composio/composio" ]]; then echo "✅ Binary installed successfully" ls -la "$HOME/.composio/composio" else echo "❌ Binary not found" exit 1 fi # Test binary execution if "$HOME/.composio/composio" --version; then echo "✅ Binary executes successfully" else echo "❌ Binary execution failed" exit 1 fi - name: Test PATH integration shell: bash run: | echo "Testing PATH integration with ${{ matrix.shell }}..." # Debug: Check what files exist and their contents echo "=== DEBUGGING ===" echo "Checking shell config files:" ls -la ~/ | grep -E '\.(bash|zsh)' || echo "No shell config files found" if [[ -f ~/.bashrc ]]; then echo "=== ~/.bashrc contents ===" cat ~/.bashrc fi if [[ -f ~/.zshrc ]]; then echo "=== ~/.zshrc contents ===" cat ~/.zshrc fi echo "=== Installation directory ===" ls -la ~/.composio/ || echo "~/.composio/ not found" echo "=== Current PATH before sourcing ===" echo "$PATH" if [[ "${{ matrix.shell }}" == "zsh" ]]; then if [[ -f ~/.zshrc ]]; then echo "Testing interactive zsh startup..." if zsh -ic 'echo "PATH after startup: $PATH"; command -v composio; composio --version; composio --help | head -5'; then echo "✅ composio found in PATH via zsh" else echo "❌ composio not found in PATH via zsh" exit 1 fi else echo "❌ ~/.zshrc not found - install script failed to create it" exit 1 fi else if [[ -f ~/.bashrc ]]; then echo "Testing interactive bash startup..." if bash -ic 'echo "PATH after startup: $PATH"; command -v composio; composio --version; composio --help | head -5'; then echo "✅ composio found in PATH via bash" else echo "❌ composio not found in PATH via bash" exit 1 fi else echo "❌ ~/.bashrc not found - install script failed to create it" exit 1 fi fi - name: Test bundled support files shell: bash run: | echo "Checking bundled support files..." install_dir="${COMPOSIO_INSTALL_DIR:-$HOME/.composio}" release_tag=$(cat "$install_dir/release-tag.txt") platform=$(uname -ms) case $platform in 'Darwin x86_64') target=darwin-x64 ;; 'Darwin arm64') target=darwin-aarch64 ;; 'Linux aarch64' | 'Linux arm64') target=linux-aarch64 ;; 'Linux x86_64') target=linux-x64 ;; *) echo "❌ Unsupported platform: $platform"; exit 1 ;; esac if [[ $target = darwin-x64 ]]; then if [[ $(sysctl -n sysctl.proc_translated 2>/dev/null) = 1 ]]; then target=darwin-aarch64 fi fi archive_name="composio-$target.zip" archive_url="https://github.com/ComposioHQ/composio/releases/download/$release_tag/$archive_name" tmpdir="$(mktemp -d)" trap 'rm -rf "$tmpdir"' EXIT curl --fail --silent --location --output "$tmpdir/$archive_name" "$archive_url" expected_paths="$( unzip -Z1 "$tmpdir/$archive_name" \ | grep -v '/$' \ | sed "s#^composio-$target/##" \ | grep -v '^composio$' \ | sort )" if [ -z "$expected_paths" ]; then echo "❌ No bundled support files found in $archive_name" exit 1 fi missing="" echo "$expected_paths" | while IFS= read -r relative_path; do if [ -f "$install_dir/$relative_path" ]; then echo "✅ Found $relative_path" else echo "MISSING:$relative_path" fi done > "$tmpdir/check_output" cat "$tmpdir/check_output" | grep -v '^MISSING:' || true missing="$(grep '^MISSING:' "$tmpdir/check_output" | sed 's/^MISSING://' || true)" if [ -n "$missing" ]; then printf '❌ Missing bundled support files after install:\n' >&2 echo "$missing" | while IFS= read -r p; do printf ' %s\n' "$p" >&2 done exit 1 fi - name: Test shell config updates run: | echo "Checking shell configuration updates..." if [[ "${{ matrix.shell }}" == "zsh" ]]; then config_file=~/.zshrc else config_file=~/.bashrc fi if grep -q "COMPOSIO_INSTALL_DIR" "$config_file"; then echo "✅ $config_file updated with COMPOSIO_INSTALL_DIR" else echo "❌ $config_file not updated with COMPOSIO_INSTALL_DIR" echo "=== $config_file contents ===" cat "$config_file" exit 1 fi if grep -q 'export PATH=.*COMPOSIO_INSTALL_DIR.*PATH' "$config_file"; then echo "✅ $config_file updated with PATH" else echo "❌ $config_file PATH not updated" echo "=== $config_file contents ===" cat "$config_file" exit 1 fi - name: Test custom install directory shell: bash env: USE_LATEST: ${{ steps.release_target.outputs.use_latest }} RELEASE_TAG: ${{ steps.release_target.outputs.release_tag }} run: | echo "Testing custom install directory..." # Clean up previous installation rm -rf ~/.composio # Test with custom directory using the same version as the workflow export COMPOSIO_INSTALL_DIR="/tmp/custom-composio" if [[ "$USE_LATEST" == "true" ]]; then bash install.sh else bash install.sh "${RELEASE_TAG}" fi # Verify custom installation if [[ -f "/tmp/custom-composio/composio" ]]; then echo "✅ Custom directory installation successful" ls -la "/tmp/custom-composio/composio" else echo "❌ Custom directory installation failed" exit 1 fi # Test execution if "/tmp/custom-composio/composio" --version; then echo "✅ Custom installation executes successfully" else echo "❌ Custom installation execution failed" exit 1 fi - name: Test uninstallation run: | echo "Testing uninstallation..." # Remove binary rm -rf ~/.composio /tmp/custom-composio # Check if binary is gone if [[ ! -f "$HOME/.composio/composio" ]]; then echo "✅ Binary removed successfully" else echo "❌ Binary removal failed" exit 1 fi - name: Test error handling shell: bash run: | echo "Testing error handling..." # Test with invalid version if bash install.sh v999.999.999 2>&1 | grep -q "Failed"; then echo "✅ Error handling works for invalid version" else echo "❌ Error handling failed" # Don't exit 1 here as this is expected to fail fi test-npm-fallback: name: Test npm Installation Fallback needs: toolchain-versions runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: os: [ubuntu-latest, macos-15] node-version: ${{ fromJSON(needs.toolchain-versions.outputs.node_install_compat) }} steps: - name: Checkout uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: Setup Node.js uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version: ${{ matrix.node-version }} - name: Test npm installation run: | echo "Testing npm installation as fallback..." # This would normally install from npm registry # For testing, we just verify the command works if command -v npm &> /dev/null; then echo "✅ npm is available" npm --version else echo "❌ npm not available" exit 1 fi test-architecture-detection: name: Test Architecture Detection runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: include: - os: ubuntu-latest arch: x64 expected: 'Linux x64' - os: depot-ubuntu-24.04-arm arch: arm64 expected: 'Linux ARM64' - os: macos-15-intel arch: x64 expected: 'macOS x64' - os: macos-15 arch: arm64 expected: 'macOS ARM64' steps: - name: Checkout uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: Test architecture detection run: | echo "Testing architecture detection for: ${{ matrix.expected }}" platform=$(uname -ms) echo "Detected platform: $platform" case $platform in 'Darwin x86_64') detected="macOS x64" ;; 'Darwin arm64') detected="macOS ARM64" ;; 'Linux x86_64') detected="Linux x64" ;; 'Linux aarch64'|'Linux arm64') detected="Linux ARM64" ;; *) echo "❌ Unknown platform: $platform" exit 1 ;; esac if [[ "$detected" == "${{ matrix.expected }}" ]]; then echo "✅ Correctly detected: $detected" else echo "❌ Detection mismatch: expected '${{ matrix.expected }}', got '$detected'" exit 1 fi test-prerequisites: name: Test Prerequisites runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: os: [ubuntu-latest, macos-15] steps: - name: Checkout uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: Test required tools run: | echo "Testing required tools..." # Test curl if command -v curl &> /dev/null; then echo "✅ curl available" curl --version | head -1 else echo "❌ curl not available" exit 1 fi # Test unzip if command -v unzip &> /dev/null; then echo "✅ unzip available" unzip -v | head -1 else echo "❌ unzip not available" exit 1 fi - name: Test without prerequisites run: | echo "Testing error handling when prerequisites are missing..." # Create a version of the script that will fail prerequisite check sed 's/command -v curl/command -v nonexistent-tool/' install.sh > test-install.sh if bash test-install.sh 2>&1 | grep -q "required to install"; then echo "✅ Prerequisites check works" else echo "❌ Prerequisites check failed" exit 1 fi rm test-install.sh summary: name: Installation Test Summary needs: [ install-script-unit-tests, test-install-script, test-npm-fallback, test-architecture-detection, test-prerequisites, ] runs-on: ubuntu-latest if: always() steps: - name: Test Results Summary run: | echo "## Installation Test Results" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY if [[ "${{ needs.install-script-unit-tests.result }}" == "success" ]]; then echo "✅ **Install Script Unit Tests**: PASSED" >> $GITHUB_STEP_SUMMARY else echo "❌ **Install Script Unit Tests**: FAILED" >> $GITHUB_STEP_SUMMARY fi if [[ "${{ needs.test-install-script.result }}" == "success" ]]; then echo "✅ **Install Script Tests**: PASSED" >> $GITHUB_STEP_SUMMARY else echo "❌ **Install Script Tests**: FAILED" >> $GITHUB_STEP_SUMMARY fi if [[ "${{ needs.test-npm-fallback.result }}" == "success" ]]; then echo "✅ **npm Fallback Tests**: PASSED" >> $GITHUB_STEP_SUMMARY else echo "❌ **npm Fallback Tests**: FAILED" >> $GITHUB_STEP_SUMMARY fi if [[ "${{ needs.test-architecture-detection.result }}" == "success" ]]; then echo "✅ **Architecture Detection**: PASSED" >> $GITHUB_STEP_SUMMARY else echo "❌ **Architecture Detection**: FAILED" >> $GITHUB_STEP_SUMMARY fi if [[ "${{ needs.test-prerequisites.result }}" == "success" ]]; then echo "✅ **Prerequisites Check**: PASSED" >> $GITHUB_STEP_SUMMARY else echo "❌ **Prerequisites Check**: FAILED" >> $GITHUB_STEP_SUMMARY fi echo "" >> $GITHUB_STEP_SUMMARY echo "**Test Matrix Coverage:**" >> $GITHUB_STEP_SUMMARY echo "- **Linux x64**: Ubuntu 22.04, Latest" >> $GITHUB_STEP_SUMMARY echo "- **Linux ARM64**: Ubuntu 22.04, 24.04 (via Depot)" >> $GITHUB_STEP_SUMMARY echo "- **macOS x64**: macOS 15 (Intel)" >> $GITHUB_STEP_SUMMARY echo "- **macOS ARM64**: macOS 14, macOS 15 (Apple Silicon)" >> $GITHUB_STEP_SUMMARY echo "- **Shells**: Bash and Zsh" >> $GITHUB_STEP_SUMMARY echo "- **Node.js**: 18, 20, 22 (npm fallback)" >> $GITHUB_STEP_SUMMARY echo "- **Total platforms**: 4 architectures across 15+ environments" >> $GITHUB_STEP_SUMMARY