diff --git a/src/agent/.devcontainer/devcontainer.json b/src/agent/.devcontainer/devcontainer.json new file mode 100644 index 000000000..b5597d661 --- /dev/null +++ b/src/agent/.devcontainer/devcontainer.json @@ -0,0 +1,35 @@ +{ + "name": "AI Agent Development Container", + "image": "mcr.microsoft.com/devcontainers/javascript-node:22", + "features": { + "ghcr.io/shengsuan/features/agent:1": { + "claudeCodeVersion": "latest", + "installCodex": true, + "installNode": true + } + }, + "customizations": { + "vscode": { + "extensions": [ + "anthropics.claude-dev" + ], + "settings": { + "terminal.integrated.defaultProfile.linux": "bash" + } + } + }, + "runArgs": [ + "--name=agent-devcontainer" + ], + "remoteEnv": { + "PATH": "${containerEnv:PATH}:${containerEnv:HOME}/.local/bin", + "SHENGSUANYUN_API_KEY": "${localEnv:SHENGSUANYUN_API_KEY}", + "ANTHROPIC_API_KEY": "${localEnv:ANTHROPIC_API_KEY}", + "OPENAI_API_KEY": "${localEnv:OPENAI_API_KEY}", + "OPENAI_BASE_URL": "${localEnv:OPENAI_BASE_URL}", + "ANTHROPIC_BASE_URL": "${localEnv:ANTHROPIC_BASE_URL}", + "CODEX_DEFAULT_MODEL": "${localEnv:CODEX_DEFAULT_MODEL}", + "CLAUDE_CODE_DEFAULT_MODEL": "${localEnv:CLAUDE_CODE_DEFAULT_MODEL}" + }, + "postCreateCommand": "agent-tools-info" +} diff --git a/src/agent/NOTES.md b/src/agent/NOTES.md new file mode 100644 index 000000000..508d55131 --- /dev/null +++ b/src/agent/NOTES.md @@ -0,0 +1,80 @@ +# Implementation Notes + +## Agent Feature + +This feature provides AI programming agent tools integration for development containers. + +### Components Installed + +1. **Claude Code CLI** + - Installed via official installation script: `curl -fsSL https://claude.ai/install.sh | bash` + - Falls back to wrapper script if installation fails + - Configurable via ANTHROPIC_API_KEY environment variable + - Reference: https://code.claude.com/docs/en/overview + +2. **OpenAI Codex CLI** + - Installed via npm: `npm install -g @openai/codex` + - Official command-line interface for OpenAI Codex + - Reference: https://github.com/openai/codex + +3. **Python and Node.js SDKs** + - Python: `openai` and `anthropic` packages + - Node.js: `openai` and `@anthropic-ai/sdk` packages + - Support for both AI platforms in multiple languages + +4. **Runtime Dependencies** + - Node.js (optional, for npm packages and Codex CLI) + - Python 3 (optional, for pip packages) + - Basic build tools and dependencies + +### Installation Process + +The install script: +1. Detects the appropriate non-root user +2. Installs runtime dependencies (Node.js, Python) if requested +3. Installs Claude Code CLI via official installer script +4. Installs OpenAI Codex CLI via npm +5. Installs Python and Node.js SDKs for both Anthropic and OpenAI +6. Creates helper scripts and configuration directories + +### Configuration + +Users need to set API keys: +- `ANTHROPIC_API_KEY` for Claude Code +- `OPENAI_API_KEY` for OpenAI tools and Codex +- `SHENGSUANYUN_API_KEY` for ShengSuanYun services (optional) + +Optional base URLs for API-compatible services: +- `OPENAI_BASE_URL` for OpenAI-compatible endpoints +- `ANTHROPIC_BASE_URL` for Anthropic-compatible endpoints + +### Helper Script + +The `agent-tools-info` command provides: +- Status of installed tools (Claude Code CLI, Codex CLI, Python/Node SDKs) +- Configuration instructions with API keys and base URLs +- Links to API key registration and documentation + +### Testing + +To test the feature: +1. Build a devcontainer with this feature +2. Run `agent-tools-info` to verify installation +3. Check that `claude` and `codex` commands are available +4. Set API keys and test basic API calls +5. Verify both Python and Node.js SDKs work correctly + +### Known Issues + +- Claude Code official installer requires internet access and may not work in restricted environments +- OpenAI Codex CLI requires Node.js/npm to be installed +- Some installations may require manual API key configuration +- Network access required during container build for package downloads + +### Future Enhancements + +- Support for additional AI coding tools (GitHub Copilot CLI, etc.) +- Pre-configured templates for common AI workflows +- Integration with more VS Code extensions +- Support for local AI models (Ollama, LM Studio, etc.) +- Automatic API key management from secret stores diff --git a/src/agent/README.md b/src/agent/README.md new file mode 100644 index 000000000..d5e249231 --- /dev/null +++ b/src/agent/README.md @@ -0,0 +1,239 @@ +# AI Agent Tools (agent) + +## Summary + +This feature installs AI programming agent tools including Claude Code CLI and OpenAI Codex integration tools to help developers leverage AI-powered coding assistance. + +## Features +- **Coding Helper**: CLI tool for configuring ShengSuanYun Coding Plan API credentials across multiple AI coding tools +- **Claude Code CLI**: Official Anthropic CLI tool for interacting with Claude AI +- **OpenAI Codex CLI**: Official OpenAI command-line coding agent +- **OpenAI Integration**: SDKs and tools for OpenAI API integration +- **Multi-language Support**: Python and Node.js SDKs for both platforms +- **Easy Configuration**: Simple environment variable configuration + +## Usage + +```json +{ + "features": { + "ghcr.io/devcontainers/features/agent:1": {} + } +} +``` + +## Options + +| Options Id | Description | Type | Default Value | +|------------|-------------|------|---------------| +| claudeCodeVersion | Version of Claude Code CLI to install. Use 'none' to skip installation. | string | latest | +| installCodex | Install OpenAI Codex integration tools and dependencies. | boolean | true | +| installNode | Install Node.js if not present (required for some AI tools). | boolean | true | +| installPython | Install Python if not present (required for OpenAI tools). | boolean | true | + +## Examples + +### Basic Installation + +Install both Claude Code and OpenAI tools with default settings: + +```json +{ + "features": { + "ghcr.io/devcontainers/features/agent:1": {} + } +} +``` + +### Claude Code Only + +Install only Claude Code CLI: + +```json +{ + "features": { + "ghcr.io/devcontainers/features/agent:1": { + "installCodex": false + } + } +} +``` + +### OpenAI Tools Only + +Install only OpenAI integration tools: + +```json +{ + "features": { + "ghcr.io/devcontainers/features/agent:1": { + "claudeCodeVersion": "none" + } + } +} +``` + +### Custom Configuration + +Skip runtime installations if already configured: + +```json +{ + "features": { + "ghcr.io/devcontainers/features/agent:1": { + "claudeCodeVersion": "latest", + "installCodex": true, + "installNode": false, + "installPython": false + } + } +} +``` + +## Configuration + +After installation, configure the tools with your API keys: + +### Environment Variables + +Add to your `.bashrc`, `.zshrc`, or container environment: + +```bash +export ANTHROPIC_API_KEY="your_anthropic_api_key_here" +export OPENAI_API_KEY="your_openai_api_key_here" +export SHENGSUANYUN_API_KEY="your_shengsuanyun_api_key_here" +export OPENAI_BASE_URL="openai_compatible_base_url" +export ANTHROPIC_BASE_URL="anthropic_compatible_base_url" +``` + +### Get API Keys + +- **胜算云 API Key**: [https://console.shengsuanyun.com/user/keys](https://console.shengsuanyun.com/user/keys) +- **Anthropic API Key**: [https://console.anthropic.com/](https://console.anthropic.com/) +- **OpenAI API Key**: [https://platform.openai.com/api-keys](https://platform.openai.com/api-keys) + +## Using the Tools + +### Claude Code CLI + +```bash +# Check Claude Code installation +claude --help + +# Start an interactive coding session +claude + +# Run a one-off task +claude "add error handling to the login function" + +# Work with specific files +claude "refactor this file" main.py +``` + +For more details, see the [Claude Code documentation](https://code.claude.com/docs). + +### OpenAI Codex CLI + +```bash +# Check Codex installation +codex --version + +# Start an interactive coding session +codex + +# Run a specific task +codex "write unit tests for the API endpoints" +``` + +For more details, see the [OpenAI Codex repository](https://github.com/openai/codex). + +### Python Usage + +```python +# Anthropic Claude +import anthropic + +client = anthropic.Anthropic(api_key="your_api_key") +message = client.messages.create( + model="claude-3-opus-20240229", + max_tokens=1024, + messages=[ + {"role": "user", "content": "Hello, Claude!"} + ] +) +print(message.content) + +# OpenAI +import openai + +openai.api_key = "your_api_key" +response = openai.ChatCompletion.create( + model="gpt-4", + messages=[{"role": "user", "content": "Hello!"}] +) +print(response.choices[0].message.content) +``` + +### Node.js Usage + +```javascript +// Anthropic Claude +import Anthropic from '@anthropic-ai/sdk'; + +const anthropic = new Anthropic({ + apiKey: process.env.ANTHROPIC_API_KEY, +}); + +const message = await anthropic.messages.create({ + model: 'claude-3-opus-20240229', + max_tokens: 1024, + messages: [{ role: 'user', content: 'Hello, Claude!' }], +}); + +console.log(message.content); + +// OpenAI +import OpenAI from 'openai'; + +const openai = new OpenAI({ + apiKey: process.env.OPENAI_API_KEY, +}); + +const completion = await openai.chat.completions.create({ + model: 'gpt-4', + messages: [{ role: 'user', content: 'Hello!' }], +}); + +console.log(completion.choices[0].message.content); +``` + +## Helper Commands + +After installation, use the following command to check installed tools: + +```bash +agent-tools-info +``` + +This displays: +- Installed AI tools and their status +- Configuration instructions +- Links to get API keys + +## Notes + +- Both Claude Code and OpenAI tools require valid API keys to function +- API usage may incur costs based on your plan with Anthropic and OpenAI +- Ensure your API keys are kept secure and not committed to version control +- Consider using `.env` files or secret management solutions for production + +## Support + +For issues and feature requests, please visit: +- [DevContainer Features Repository](https://github.com/devcontainers/features) +- [Anthropic Documentation](https://docs.anthropic.com/) +- [OpenAI Documentation](https://platform.openai.com/docs) + +--- + +*This feature is maintained by the DevContainers community.* diff --git a/src/agent/configure-runtime.sh b/src/agent/configure-runtime.sh new file mode 100644 index 000000000..8543b7437 --- /dev/null +++ b/src/agent/configure-runtime.sh @@ -0,0 +1,54 @@ +#!/usr/bin/env bash +#------------------------------------------------------------------------------------------------------------- +# Runtime Configuration Script for AI Agent Tools +# This script should be run at container startup (postCreateCommand/postStartCommand) +# to configure API keys without leaking them into the image layers. +#------------------------------------------------------------------------------------------------------------- + +SHENGSUANYUN_API_KEY=${SHENGSUANYUN_API_KEY:-"none"} +CODEX_DEFAULT_MODEL=${CODEXDEFAULTMODEL:-"openai/gpt-5.3-codex"} +CLAUDE_CODE_DEFAULT_MODEL=${CLAUDECODEDEFAULTMODEL:-"anthropic/claude-sonnet-4.6"} + +echo "Configuring AI Agent Tools at runtime..." + +if [ "${SHENGSUANYUN_API_KEY}" != "none" ]; then + echo "Configuring Coding Helper with ShengSuanYun API key..." + + # Configure base Coding Helper + if type coding-helper > /dev/null 2>&1; then + coding-helper custom \ + -url https://router.shengsuanyun.com/api/v1 \ + -k ${SHENGSUANYUN_API_KEY} \ + -m ${CLAUDE_CODE_DEFAULT_MODEL} \ + -label 胜算云 + else + echo "Warning: coding-helper not found, skipping configuration" + fi + + # Configure Codex if available + if type codex > /dev/null 2>&1 && type coding-helper > /dev/null 2>&1; then + echo "Configuring Codex with ShengSuanYun API key..." + coding-helper custom \ + -url https://router.shengsuanyun.com/api/v1 \ + -k ${SHENGSUANYUN_API_KEY} \ + -m ${CODEX_DEFAULT_MODEL} \ + -t codex \ + -label 胜算云-codex + fi + + # Configure Claude Code CLI if available + if type claude > /dev/null 2>&1 && type coding-helper > /dev/null 2>&1; then + echo "Configuring Claude Code CLI with ShengSuanYun API key..." + coding-helper custom \ + -url https://router.shengsuanyun.com/api/v1 \ + -k ${SHENGSUANYUN_API_KEY} \ + -m ${CLAUDE_CODE_DEFAULT_MODEL} \ + -t claude \ + -label 胜算云-claude + fi + + echo "Runtime configuration complete." +else + echo "No SHENGSUANYUN_API_KEY provided, skipping API configuration." + echo "Set SHENGSUANYUN_API_KEY environment variable to configure automatically." +fi diff --git a/src/agent/devcontainer-feature.json b/src/agent/devcontainer-feature.json new file mode 100644 index 000000000..8c556ac70 --- /dev/null +++ b/src/agent/devcontainer-feature.json @@ -0,0 +1,47 @@ +{ + "id": "agent", + "version": "2.0.6", + "name": "AI Agent Tools", + "documentationURL": "https://github.com/devcontainers/features/tree/main/src/agent", + "description": "Installs AI programming agent tools including Claude Code CLI, OpenAI Codex CLI, and integration SDKs.", + "options": { + "claudeCodeVersion": { + "type": "string", + "proposals": [ + "latest", + "none" + ], + "default": "latest", + "description": "Version of Claude Code CLI to install. Use 'none' to skip installation." + }, + "installCodex": { + "type": "boolean", + "default": true, + "description": "Install OpenAI Codex CLI and integration tools (SDKs for Python and Node.js)." + }, + "installNode": { + "type": "boolean", + "default": true, + "description": "Install Node.js if not present (required for some AI tools)." + } + }, + "customizations": { + "vscode": { + "settings": { + "github.copilot.chat.codeGeneration.instructions": [ + { + "text": "This dev container includes Claude Code CLI and OpenAI Codex CLI. Claude Code can be accessed via the `claude` command. OpenAI Codex can be accessed via the `codex` command. Both platforms have Python and Node.js SDKs available." + } + ] + }, + "extensions": [ + "anthropics.claude-dev" + ] + } + }, + "installsAfter": [ + "ghcr.io/devcontainers/features/common-utils", + "ghcr.io/devcontainers/features/git", + "ghcr.io/devcontainers/features/node" + ] +} diff --git a/src/agent/install.sh b/src/agent/install.sh new file mode 100755 index 000000000..bdd17a856 --- /dev/null +++ b/src/agent/install.sh @@ -0,0 +1,326 @@ +#!/usr/bin/env bash + +CLAUDE_CODE_VERSION=${CLAUDECODEVERSION:-"latest"} +INSTALL_CODEX=${INSTALLCODEX:-"true"} +INSTALL_NODE=${INSTALLNODE:-"true"} +SHENGSUANYUN_API_KEY=${SHENGSUANYUN_API_KEY:-"none"} +CODEX_DEFAULT_MODEL=${CODEXDEFAULTMODEL:-"openai/gpt-5.3-codex"} +CLAUDE_CODE_DEFAULT_MODEL=${CLAUDECODEDEFAULTMODEL:-"anthropic/claude-sonnet-4.6"} + +set -e + +# Clean up +rm -rf /var/lib/apt/lists/* + +if [ "$(id -u)" -ne 0 ]; then + echo -e 'Script must be run as root. Use sudo, su, or add "USER root" to your Dockerfile before running this script.' + exit 1 +fi + +apt_get_update() { + if [ "$(find /var/lib/apt/lists/* | wc -l)" = "0" ]; then + echo "Running apt-get update..." + apt-get update -y + fi +} + +# Checks if packages are installed and installs them if not +check_packages() { + if ! dpkg -s "$@" > /dev/null 2>&1; then + apt_get_update + apt-get -y install --no-install-recommends "$@" + fi +} + +export DEBIAN_FRONTEND=noninteractive + +# Install basic dependencies +echo "Installing basic dependencies..." +check_packages curl ca-certificates wget gnupg2 + +# Determine the appropriate non-root user +USERNAME="${USERNAME:-"${_REMOTE_USER:-"automatic"}"}" +if [ "${USERNAME}" = "auto" ] || [ "${USERNAME}" = "automatic" ]; then + USERNAME="" + POSSIBLE_USERS=("vscode" "node" "codespace" "$(awk -v val=1000 -F ":" '$3==val{print $1}' /etc/passwd)") + for CURRENT_USER in "${POSSIBLE_USERS[@]}"; do + if [ -n "${CURRENT_USER}" ] && id -u "${CURRENT_USER}" > /dev/null 2>&1; then + USERNAME="${CURRENT_USER}" + break + fi + done + if [ -z "${USERNAME}" ]; then + USERNAME=root + fi +elif [ "${USERNAME}" = "none" ] || ! id -u "${USERNAME}" > /dev/null 2>&1; then + USERNAME=root +fi + +echo "Installing for user: ${USERNAME}" + +# Install Node.js if requested and not present +if [ "${INSTALL_NODE}" = "true" ] && ! type node > /dev/null 2>&1; then + echo "Installing Node.js..." + check_packages nodejs npm +fi + +# Install Claude Code CLI +if [ "${CLAUDE_CODE_VERSION}" != "none" ]; then + echo "Installing Claude Code CLI..." + + # Install via official installation script + # Reference: https://code.claude.com/docs/en/overview + if ! type claude > /dev/null 2>&1; then + echo "Installing Claude Code using official installer..." + npm install -g @anthropic-ai/claude-code|| echo "(!) Claude Code installation failed, creating fallback wrapper..." + fi + + # Fallback: Create a wrapper script if installation failed + # Check both PATH and common installation locations + if ! type claude > /dev/null 2>&1 && \ + ! [ -f "/usr/local/bin/claude" ] && \ + ! [ -f "$HOME/.local/bin/claude" ] && \ + ! [ -f "/home/$USERNAME/.local/bin/claude" ]; then + echo "Creating Claude Code CLI wrapper..." + cat > /usr/local/bin/claude << 'EOF' +#!/bin/bash +echo "Claude Code CLI" +echo "" +echo "Note: Claude Code installation may have failed." +echo "Please try manual installation:" +echo " curl -fsSL https://claude.ai/install.sh | bash" +echo "" +echo "For configuration, set your API key:" +echo " export ANTHROPIC_API_KEY=your_api_key_here" +echo "" +echo "Visit https://console.anthropic.com/ to get your API key" +echo "Documentation: https://code.claude.com/docs" +EOF + chmod +x /usr/local/bin/claude + elif [ -f "/home/$USERNAME/.local/bin/claude" ] && ! type claude > /dev/null 2>&1; then + # Claude installed to ~/.local/bin but not in PATH, create symlink + echo "Creating symlink to Claude Code CLI..." + ln -sf "/home/$USERNAME/.local/bin/claude" /usr/local/bin/claude + fi + + echo "Claude Code CLI installation complete." +fi + +# Install OpenAI Codex integration tools +if [ "${INSTALL_CODEX}" = "true" ]; then + echo "Installing OpenAI Codex integration tools..." + + # Install OpenAI Codex CLI + # Reference: https://github.com/openai/codex + if type npm > /dev/null 2>&1 && ! type codex > /dev/null 2>&1; then + echo "Installing OpenAI Codex CLI..." + npm install -g @openai/codex || echo "(!) Codex CLI installation failed" + fi + + # Install Python OpenAI and Anthropic SDKs + if type pip3 > /dev/null 2>&1; then + echo "Installing Python SDKs..." + if [ "${USERNAME}" = "root" ]; then + pip3 install --upgrade openai anthropic + else + su - "${USERNAME}" -c "pip3 install --user --upgrade openai anthropic" + fi + fi + + # Install Node.js OpenAI and Anthropic SDKs + if type npm > /dev/null 2>&1; then + echo "Installing Node.js SDKs..." + npm install -g openai @anthropic-ai/sdk + fi + + echo "OpenAI Codex tools installation complete." +fi + +echo "Installing Coding Helper integration tools..." +# Reference: https://github.com/shengsuan/coding-helper +if type npm > /dev/null 2>&1; then + echo "Installing/updating Coding Helper to latest version..." + npm install -g @coohu/coding-helper@latest || echo "(!) Coding Helper installation failed" +fi +echo "Coding Helper tools installation complete." + +# Create configuration directory +if [ "${USERNAME}" != "root" ]; then + USER_HOME=$(eval echo ~${USERNAME}) + mkdir -p "${USER_HOME}/.config/agent-tools" + chown -R ${USERNAME}:${USERNAME} "${USER_HOME}/.config/agent-tools" 2>/dev/null || true +fi + +# Install runtime configuration script +# Note: API key configuration has been moved to configure-runtime.sh +# This prevents API keys from being leaked into the image layers +echo "Installing runtime configuration script..." +cat > /usr/local/bin/configure-agent-runtime << CONFIGURE_EOF +#!/usr/bin/env bash +#------------------------------------------------------------------------------------------------------------- +# Runtime Configuration Script for AI Agent Tools +# This script should be run at container startup (postCreateCommand/postStartCommand) +# to configure API keys without leaking them into the image layers. +#------------------------------------------------------------------------------------------------------------- + +SHENGSUANYUN_API_KEY=\${SHENGSUANYUN_API_KEY:-"none"} +TARGET_USER="${USERNAME}" + +echo "Configuring AI Agent Tools at runtime..." +echo "Target user: \${TARGET_USER}" + +# Function to run command as the target user +run_as_user() { + if [ "\${TARGET_USER}" = "root" ] || [ "\$(whoami)" = "\${TARGET_USER}" ]; then + eval "\$@" + else + su - "\${TARGET_USER}" -c "\$*" + fi +} + +if [ "\${SHENGSUANYUN_API_KEY}" != "none" ]; then + echo "Configuring Coding Helper with ShengSuanYun API key..." + + # Configure base Coding Helper + if type coding-helper > /dev/null 2>&1; then + run_as_user "coding-helper custom \\ + -url https://router.shengsuanyun.com/api/v1 \\ + -k \${SHENGSUANYUN_API_KEY} \\ + -m \${CLAUDE_CODE_DEFAULT_MODEL} \\ + -label 胜算云" + else + echo "Warning: coding-helper not found, skipping configuration" + fi + + # Configure Codex if available + if type codex > /dev/null 2>&1 && type coding-helper > /dev/null 2>&1; then + echo "Configuring Codex with ShengSuanYun API key..." + run_as_user "coding-helper custom \\ + -url https://router.shengsuanyun.com/api/v1 \\ + -k \${SHENGSUANYUN_API_KEY} \\ + -m \${CODEX_DEFAULT_MODEL} \\ + -t codex \\ + -label 胜算云-codex" + fi + + # Configure Claude Code CLI if available + if type claude > /dev/null 2>&1 && type coding-helper > /dev/null 2>&1; then + echo "Configuring Claude Code CLI with ShengSuanYun API key..." + run_as_user "coding-helper custom \\ + -url https://router.shengsuanyun.com/api/v1 \\ + -k \${SHENGSUANYUN_API_KEY} \\ + -m \${CLAUDE_CODE_DEFAULT_MODEL} \\ + -t claude \\ + -label 胜算云-claude" + fi + + echo "Runtime configuration complete for user: \${TARGET_USER}" +else + echo "No SHENGSUANYUN_API_KEY provided, skipping API configuration." + echo "Set SHENGSUANYUN_API_KEY environment variable to configure automatically." +fi +CONFIGURE_EOF + +chmod +x /usr/local/bin/configure-agent-runtime +echo "Runtime configuration script installed at: /usr/local/bin/configure-agent-runtime" + +# Create a helper script for AI agent tools +cat > /usr/local/bin/agent-tools-info << 'EOF' +#!/bin/bash +echo "===================================" +echo "AI Agent Tools - Installation Info" +echo "===================================" +echo "" +echo "Installed Tools:" +echo "----------------" + +if command -v claude &> /dev/null; then + echo "✓ Claude Code CLI" + echo " Usage: claude [command]" + echo " Config: Set ANTHROPIC_API_KEY environment variable" + echo " Docs: https://code.claude.com/docs" +else + echo "✗ Claude Code CLI (not found)" +fi + +if command -v codex &> /dev/null; then + echo "✓ OpenAI Codex CLI" + echo " Usage: codex [command]" + echo " Config: Sign in or set OPENAI_API_KEY" + echo " Docs: https://github.com/openai/codex" +else + echo "✗ OpenAI Codex CLI (not installed)" +fi + +if command -v coding-helper &> /dev/null; then + echo "✓ Coding Helper" + echo " Usage: coding-helper [command]" + echo " Config: Set API keys as environment variables" + echo " Docs: https://github.com/shengsuan/coding-helper" +else + echo "✗ Coding Helper (not installed)" +fi + +if python3 -c "import openai" 2>/dev/null; then + echo "✓ OpenAI Python SDK" + echo " Usage: import openai in Python" +else + echo "✗ OpenAI Python SDK (not installed)" +fi + +if python3 -c "import anthropic" 2>/dev/null; then + echo "✓ Anthropic Python SDK" + echo " Usage: import anthropic in Python" +else + echo "✗ Anthropic Python SDK (not installed)" +fi + +if command -v node &> /dev/null && node -e "require('openai')" 2>/dev/null; then + echo "✓ OpenAI Node.js SDK" + echo " Usage: import OpenAI from 'openai'" +else + echo "✗ OpenAI Node.js SDK (not installed)" +fi + +if command -v node &> /dev/null && node -e "require('@anthropic-ai/sdk')" 2>/dev/null; then + echo "✓ Anthropic Node.js SDK" + echo " Usage: import Anthropic from '@anthropic-ai/sdk'" +else + echo "✗ Anthropic Node.js SDK (not installed)" +fi + +echo "" +echo "Configuration:" +echo "--------------" +echo "Set your API keys as environment variables:" +echo " export ANTHROPIC_API_KEY=your_anthropic_key" +echo " export OPENAI_API_KEY=your_openai_key" +echo " export SHENGSUANYUN_API_KEY=your_shengsuanyun_key" +echo "" +echo "Optional base URLs for API-compatible services:" +echo " export OPENAI_BASE_URL=openai_compatible_base_url" +echo " export ANTHROPIC_BASE_URL=anthropic_compatible_base_url" +echo "" +echo "Get API keys from:" +echo " - ShengSuanYun: https://console.shengsuanyun.com/user/keys" +echo " - Anthropic: https://console.anthropic.com/" +echo " - OpenAI: https://platform.openai.com/api-keys" +echo "" +echo "Documentation:" +echo " - Claude Code: https://code.claude.com/docs" +echo " - OpenAI Codex: https://github.com/openai/codex" +echo " - Coding Helper: https://github.com/shengsuan/coding-helper" +EOF + +chmod +x /usr/local/bin/agent-tools-info + +echo "" +echo "=========================================" +echo "AI Agent Tools installation complete!" +echo "=========================================" +echo "" +echo "Run 'agent-tools-info' to see installed tools and configuration instructions." +echo "" + +# Clean up +rm -rf /var/lib/apt/lists/* diff --git a/src/go-tidy/README.md b/src/go-tidy/README.md new file mode 100644 index 000000000..e7593a877 --- /dev/null +++ b/src/go-tidy/README.md @@ -0,0 +1,207 @@ +# Go (Tidy - without golangci-lint) + +## Summary + +*Installs Go and common Go utilities without golangci-lint. Auto-detects latest version and installs needed dependencies.* + +## Description + +This feature installs the Go programming language and commonly used Go development tools, **excluding golangci-lint** to avoid checksum verification issues that sometimes occur during container builds. + +## Why This Feature? + +The original Go feature includes golangci-lint installation which occasionally fails with checksum verification errors: + +``` +golangci/golangci-lint err hash_sha256_verify checksum for '/tmp/tmp.BeV46TqaJP/golangci-lint-2.12.2-linux-amd64.tar.gz' did not verify +``` + +This "tidy" version removes golangci-lint installation to provide a more stable build experience. You can install golangci-lint separately after the container is built if needed. + +## Example Usage + +```json +"features": { + "ghcr.io/shengsuan/features/go-tidy:1": { + "version": "latest" + } +} +``` + +## Options + +| Options Id | Description | Type | Default Value | +|-----|-----|-----|-----| +| version | Select or enter a Go version to install | string | latest | + +## Installed Tools + +This feature installs the following Go tools: + +- Go runtime and compiler +- gopls (Go language server) +- staticcheck +- golint +- revive +- dlv (Delve debugger) +- gomodifytags +- goplay +- gotests +- impl + +**Note:** golangci-lint is intentionally excluded from this feature. + +## Customizations + +### VS Code Extensions + +- `golang.Go` + +## OS Support + +This feature has been tested on the following operating systems: + +- Debian/Ubuntu +- RHEL/Fedora/CentOS +- Mariner + +# 如何使用 go-tidy Feature + +## 背景 + +由于原始的 Go feature 在安装 golangci-lint 时经常遇到 checksum 验证错误: + +``` +golangci/golangci-lint err hash_sha256_verify checksum for '/tmp/tmp.BeV46TqaJP/golangci-lint-2.12.2-linux-amd64.tar.gz' did not verify +``` + +为了解决这个问题,我们创建了 `go-tidy` feature,它提供了与标准 Go feature 相同的功能,但排除了 golangci-lint 的安装。 + +## 使用方法 + +### 在 devcontainer.json 中使用 + +将以下内容添加到你的 `.devcontainer/devcontainer.json` 文件中: + +```json +{ + "name": "My Go Project", + "image": "mcr.microsoft.com/devcontainers/base:ubuntu", + "features": { + "ghcr.io/shengsuan/features/go-tidy:1": { + "version": "latest" + } + } +} +``` + +### 指定 Go 版本 + +```json +{ + "features": { + "ghcr.io/shengsuan/features/go-tidy:1": { + "version": "1.23" + } + } +} +``` + +### 与其他 features 组合使用 + +```json +{ + "name": "My Go Project with Docker", + "image": "mcr.microsoft.com/devcontainers/base:ubuntu", + "features": { + "ghcr.io/shengsuan/features/go-tidy:1": { + "version": "latest" + }, + "ghcr.io/devcontainers/features/docker-in-docker:2": { + "version": "latest" + } + } +} +``` + +## 包含的工具 + +该 feature 会安装以下 Go 工具: + +- Go 运行时和编译器 +- gopls (Go 语言服务器) +- staticcheck (静态代码分析) +- golint (代码风格检查) +- revive (Go 代码检查器) +- dlv (Delve 调试器) +- gomodifytags (修改 struct 标签) +- goplay (Go Playground 客户端) +- gotests (生成测试代码) +- impl (实现接口存根) + +**注意:** golangci-lint 不包含在此 feature 中。 + +## 如果需要 golangci-lint + +如果你的项目确实需要 golangci-lint,可以在容器启动后手动安装: + +```bash +# 在容器内运行 +curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin +``` + +或者在 `devcontainer.json` 的 `postCreateCommand` 中添加: + +```json +{ + "features": { + "ghcr.io/shengsuan/features/go-tidy:1": { + "version": "latest" + } + }, + "postCreateCommand": "curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin" +} +``` + +## 发布状态 + +该 feature 已提交到 GitHub 仓库,GitHub Actions 会自动构建并发布到: + +- Registry: `ghcr.io/shengsuan/features/go-tidy` +- Tag: `1` (major version), `1.0` (minor version), `1.0.0` (patch version) + +等待 GitHub Actions 工作流完成后,你就可以在项目中使用该 feature 了。 + +## 查看发布状态 + +访问以下链接查看 GitHub Actions 工作流状态: + +https://github.com/shengsuan/features/actions + +选择 "Release dev container features" 工作流查看最新的发布状态。 + +## 环境变量 + +该 feature 会自动设置以下环境变量: + +- `GOROOT=/usr/local/go` +- `GOPATH=/go` +- `PATH` 包含 `/usr/local/go/bin` 和 `/go/bin` + +## 支持的平台 + +- Debian/Ubuntu +- RHEL/Fedora/AlmaLinux/CentOS +- Azure Linux (Mariner) + +## 相关链接 + +- [原始 Go Feature](https://github.com/devcontainers/features/tree/main/src/go) +- [Dev Container Features 规范](https://containers.dev/implementors/features/) +- [GitHub 仓库](https://github.com/shengsuan/features) + + +## References + +- [Go Official Website](https://golang.org/) +- [Original Go Feature](https://github.com/devcontainers/features/tree/main/src/go) diff --git a/src/go-tidy/devcontainer-feature.json b/src/go-tidy/devcontainer-feature.json new file mode 100644 index 000000000..ccb682888 --- /dev/null +++ b/src/go-tidy/devcontainer-feature.json @@ -0,0 +1,49 @@ +{ + "id": "go-tidy", + "version": "1.0.0", + "name": "Go (Tidy - without golangci-lint)", + "documentationURL": "https://github.com/shengsuan/features/tree/main/src/go-tidy", + "description": "Installs Go and common Go utilities without golangci-lint. Auto-detects latest version and installs needed dependencies.", + "options": { + "version": { + "type": "string", + "proposals": [ + "latest", + "none", + "1.24", + "1.23" + ], + "default": "latest", + "description": "Select or enter a Go version to install" + } + }, + "init": true, + "customizations": { + "vscode": { + "extensions": [ + "golang.Go" + ], + "settings": { + "github.copilot.chat.codeGeneration.instructions": [ + { + "text": "This dev container includes Go and common Go utilities pre-installed and available on the `PATH`, along with the Go language extension for Go development." + } + ] + } + } + }, + "containerEnv": { + "GOROOT": "/usr/local/go", + "GOPATH": "/go", + "PATH": "/usr/local/go/bin:/go/bin:${PATH}" + }, + "capAdd": [ + "SYS_PTRACE" + ], + "securityOpt": [ + "seccomp=unconfined" + ], + "installsAfter": [ + "ghcr.io/devcontainers/features/common-utils" + ] +} diff --git a/src/go-tidy/install.sh b/src/go-tidy/install.sh new file mode 100755 index 000000000..3fabb5765 --- /dev/null +++ b/src/go-tidy/install.sh @@ -0,0 +1,340 @@ +#!/usr/bin/env bash +#------------------------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See https://go.microsoft.com/fwlink/?linkid=2090316 for license information +#------------------------------------------------------------------------------------------------------------- +# +# Docs: https://github.com/microsoft/vscode-dev-containers/blob/main/script-library/docs/go.md +# Maintainer: The VS Code and Codespaces Teams + +TARGET_GO_VERSION="${VERSION:-"latest"}" + +TARGET_GOROOT="${TARGET_GOROOT:-"/usr/local/go"}" +TARGET_GOPATH="${TARGET_GOPATH:-"/go"}" +USERNAME="${USERNAME:-"${_REMOTE_USER:-"automatic"}"}" +INSTALL_GO_TOOLS="${INSTALL_GO_TOOLS:-"true"}" + +# https://www.google.com/linuxrepositories/ +GO_GPG_KEY_URI="https://dl.google.com/linux/linux_signing_key.pub" + +set -e + +if [ "$(id -u)" -ne 0 ]; then + echo -e 'Script must be run as root. Use sudo, su, or add "USER root" to your Dockerfile before running this script.' + exit 1 +fi + +# Bring in ID, ID_LIKE, VERSION_ID, VERSION_CODENAME +. /etc/os-release +# Get an adjusted ID independent of distro variants +MAJOR_VERSION_ID=$(echo ${VERSION_ID} | cut -d . -f 1) +if [ "${ID}" = "debian" ] || [ "${ID_LIKE}" = "debian" ]; then + ADJUSTED_ID="debian" +elif [[ "${ID}" = "rhel" || "${ID}" = "fedora" || "${ID}" = "mariner" || "${ID_LIKE}" = *"rhel"* || "${ID_LIKE}" = *"fedora"* || "${ID_LIKE}" = *"mariner"* ]]; then + ADJUSTED_ID="rhel" + if [[ "${ID}" = "rhel" ]] || [[ "${ID}" = *"alma"* ]] || [[ "${ID}" = *"rocky"* ]]; then + VERSION_CODENAME="rhel${MAJOR_VERSION_ID}" + else + VERSION_CODENAME="${ID}${MAJOR_VERSION_ID}" + fi +else + echo "Linux distro ${ID} not supported." + exit 1 +fi + +if [ "${ADJUSTED_ID}" = "rhel" ] && [ "${VERSION_CODENAME-}" = "centos7" ]; then + # As of 1 July 2024, mirrorlist.centos.org no longer exists. + # Update the repo files to reference vault.centos.org. + sed -i s/mirror.centos.org/vault.centos.org/g /etc/yum.repos.d/*.repo + sed -i s/^#.*baseurl=http/baseurl=http/g /etc/yum.repos.d/*.repo + sed -i s/^mirrorlist=http/#mirrorlist=http/g /etc/yum.repos.d/*.repo +fi + +# Setup INSTALL_CMD & PKG_MGR_CMD +if type apt-get > /dev/null 2>&1; then + PKG_MGR_CMD=apt-get + INSTALL_CMD="${PKG_MGR_CMD} -y install --no-install-recommends" +elif type microdnf > /dev/null 2>&1; then + PKG_MGR_CMD=microdnf + INSTALL_CMD="${PKG_MGR_CMD} ${INSTALL_CMD_ADDL_REPOS} -y install --refresh --best --nodocs --noplugins --setopt=install_weak_deps=0" +elif type dnf > /dev/null 2>&1; then + PKG_MGR_CMD=dnf + INSTALL_CMD="${PKG_MGR_CMD} ${INSTALL_CMD_ADDL_REPOS} -y install --refresh --best --nodocs --noplugins --setopt=install_weak_deps=0" +else + PKG_MGR_CMD=yum + INSTALL_CMD="${PKG_MGR_CMD} ${INSTALL_CMD_ADDL_REPOS} -y install --noplugins --setopt=install_weak_deps=0" +fi + +# Clean up +clean_up() { + case ${ADJUSTED_ID} in + debian) + rm -rf /var/lib/apt/lists/* + ;; + rhel) + rm -rf /var/cache/dnf/* /var/cache/yum/* + rm -rf /tmp/yum.log + rm -rf ${GPG_INSTALL_PATH} + ;; + esac +} +clean_up + + +# Figure out correct version of a three part version number is not passed +find_version_from_git_tags() { + local variable_name=$1 + local requested_version=${!variable_name} + if [ "${requested_version}" = "none" ]; then return; fi + local repository=$2 + local prefix=${3:-"tags/v"} + local separator=${4:-"."} + local last_part_optional=${5:-"false"} + if [ "$(echo "${requested_version}" | grep -o "." | wc -l)" != "2" ]; then + local escaped_separator=${separator//./\\.} + local last_part + if [ "${last_part_optional}" = "true" ]; then + last_part="(${escaped_separator}[0-9]+)?" + else + last_part="${escaped_separator}[0-9]+" + fi + local regex="${prefix}\\K[0-9]+${escaped_separator}[0-9]+${last_part}$" + local version_list="$(git ls-remote --tags ${repository} | grep -oP "${regex}" | tr -d ' ' | tr "${separator}" "." | sort -rV)" + if [ "${requested_version}" = "latest" ] || [ "${requested_version}" = "current" ] || [ "${requested_version}" = "lts" ]; then + declare -g ${variable_name}="$(echo "${version_list}" | head -n 1)" + else + set +e + declare -g ${variable_name}="$(echo "${version_list}" | grep -E -m 1 "^${requested_version//./\\.}([\\.\\s]|$)")" + set -e + fi + fi + if [ -z "${!variable_name}" ] || ! echo "${version_list}" | grep "^${!variable_name//./\\.}$" > /dev/null 2>&1; then + echo -e "Invalid ${variable_name} value: ${requested_version}\nValid values:\n${version_list}" >&2 + exit 1 + fi + echo "${variable_name}=${!variable_name}" +} + +pkg_mgr_update() { + case $ADJUSTED_ID in + debian) + if [ "$(find /var/lib/apt/lists/* | wc -l)" = "0" ]; then + echo "Running apt-get update..." + ${PKG_MGR_CMD} update -y + fi + ;; + rhel) + if [ ${PKG_MGR_CMD} = "microdnf" ]; then + if [ "$(ls /var/cache/yum/* 2>/dev/null | wc -l)" = 0 ]; then + echo "Running ${PKG_MGR_CMD} makecache ..." + ${PKG_MGR_CMD} makecache + fi + else + if [ "$(ls /var/cache/${PKG_MGR_CMD}/* 2>/dev/null | wc -l)" = 0 ]; then + echo "Running ${PKG_MGR_CMD} check-update ..." + set +e + ${PKG_MGR_CMD} check-update + rc=$? + if [ $rc != 0 ] && [ $rc != 100 ]; then + exit 1 + fi + set -e + fi + fi + ;; + esac +} + +# Checks if packages are installed and installs them if not +check_packages() { + case ${ADJUSTED_ID} in + debian) + if ! dpkg -s "$@" > /dev/null 2>&1; then + pkg_mgr_update + ${INSTALL_CMD} "$@" + fi + ;; + rhel) + if ! rpm -q "$@" > /dev/null 2>&1; then + pkg_mgr_update + ${INSTALL_CMD} "$@" + fi + ;; + esac +} + +# Ensure that login shells get the correct path if the user updated the PATH using ENV. +rm -f /etc/profile.d/00-restore-env.sh +echo "export PATH=${PATH//$(sh -lc 'echo $PATH')/\$PATH}" > /etc/profile.d/00-restore-env.sh +chmod +x /etc/profile.d/00-restore-env.sh + +# Some distributions do not install awk by default (e.g. Mariner) +if ! type awk >/dev/null 2>&1; then + check_packages awk +fi + +# Determine the appropriate non-root user +if [ "${USERNAME}" = "auto" ] || [ "${USERNAME}" = "automatic" ]; then + USERNAME="" + POSSIBLE_USERS=("vscode" "node" "codespace" "$(awk -v val=1000 -F ":" '$3==val{print $1}' /etc/passwd)") + for CURRENT_USER in "${POSSIBLE_USERS[@]}"; do + if id -u ${CURRENT_USER} > /dev/null 2>&1; then + USERNAME=${CURRENT_USER} + break + fi + done + if [ "${USERNAME}" = "" ]; then + USERNAME=root + fi +elif [ "${USERNAME}" = "none" ] || ! id -u ${USERNAME} > /dev/null 2>&1; then + USERNAME=root +fi + +export DEBIAN_FRONTEND=noninteractive + +check_packages ca-certificates gnupg2 tar gcc make pkg-config + +if [ $ADJUSTED_ID = "debian" ]; then + check_packages g++ libc6-dev +else + check_packages gcc-c++ glibc-devel +fi +# Install curl, git, other dependencies if missing +if ! type curl > /dev/null 2>&1; then + check_packages curl +fi +if ! type git > /dev/null 2>&1; then + check_packages git +fi +# Some systems, e.g. Mariner, still a few more packages +if ! type as > /dev/null 2>&1; then + check_packages binutils +fi +if ! [ -f /usr/include/linux/errno.h ]; then + check_packages kernel-headers +fi +# Minimal RHEL install may need findutils installed +if ! [ -f /usr/bin/find ]; then + check_packages findutils +fi + +# Get closest match for version number specified +find_version_from_git_tags TARGET_GO_VERSION "https://go.googlesource.com/go" "tags/go" "." "true" + +architecture="$(uname -m)" +case $architecture in + x86_64) architecture="amd64";; + aarch64 | armv8*) architecture="arm64";; + aarch32 | armv7* | armvhf*) architecture="armv6l";; + i?86) architecture="386";; + *) echo "(!) Architecture $architecture unsupported"; exit 1 ;; +esac + +# Install Go +umask 0002 +if ! cat /etc/group | grep -e "^golang:" > /dev/null 2>&1; then + groupadd -r golang +fi +usermod -a -G golang "${USERNAME}" +mkdir -p "${TARGET_GOROOT}" "${TARGET_GOPATH}" + +if [[ "${TARGET_GO_VERSION}" != "none" ]] && [[ "$(go version 2>/dev/null)" != *"${TARGET_GO_VERSION}"* ]]; then + # Use a temporary location for gpg keys to avoid polluting image + export GNUPGHOME="/tmp/tmp-gnupg" + mkdir -p ${GNUPGHOME} + chmod 700 ${GNUPGHOME} + curl -sSL -o /tmp/tmp-gnupg/golang_key "${GO_GPG_KEY_URI}" + gpg -q --import /tmp/tmp-gnupg/golang_key + echo "Downloading Go ${TARGET_GO_VERSION}..." + set +e + curl -fsSL -o /tmp/go.tar.gz "https://golang.org/dl/go${TARGET_GO_VERSION}.linux-${architecture}.tar.gz" + exit_code=$? + set -e + if [ "$exit_code" != "0" ]; then + echo "(!) Download failed." + # Try one break fix version number less if we get a failure. Use "set +e" since "set -e" can cause failures in valid scenarios. + set +e + major="$(echo "${TARGET_GO_VERSION}" | grep -oE '^[0-9]+' || echo '')" + minor="$(echo "${TARGET_GO_VERSION}" | grep -oP '^[0-9]+\.\K[0-9]+' || echo '')" + breakfix="$(echo "${TARGET_GO_VERSION}" | grep -oP '^[0-9]+\.[0-9]+\.\K[0-9]+' 2>/dev/null || echo '')" + # Handle Go's odd version pattern where "0" releases omit the last part + if [ "${breakfix}" = "" ] || [ "${breakfix}" = "0" ]; then + ((minor=minor-1)) + TARGET_GO_VERSION="${major}.${minor}" + # Look for latest version from previous minor release + find_version_from_git_tags TARGET_GO_VERSION "https://go.googlesource.com/go" "tags/go" "." "true" + else + ((breakfix=breakfix-1)) + if [ "${breakfix}" = "0" ]; then + TARGET_GO_VERSION="${major}.${minor}" + else + TARGET_GO_VERSION="${major}.${minor}.${breakfix}" + fi + fi + set -e + echo "Trying ${TARGET_GO_VERSION}..." + curl -fsSL -o /tmp/go.tar.gz "https://golang.org/dl/go${TARGET_GO_VERSION}.linux-${architecture}.tar.gz" + fi + curl -fsSL -o /tmp/go.tar.gz.asc "https://golang.org/dl/go${TARGET_GO_VERSION}.linux-${architecture}.tar.gz.asc" + gpg --verify /tmp/go.tar.gz.asc /tmp/go.tar.gz + echo "Extracting Go ${TARGET_GO_VERSION}..." + tar -xzf /tmp/go.tar.gz -C "${TARGET_GOROOT}" --strip-components=1 + rm -rf /tmp/go.tar.gz /tmp/go.tar.gz.asc /tmp/tmp-gnupg +else + echo "(!) Go is already installed with version ${TARGET_GO_VERSION}. Skipping." +fi + +# Install Go tools that are isImportant && !replacedByGopls based on +# https://github.com/golang/vscode-go/blob/v0.38.0/src/goToolsInformation.ts +GO_TOOLS="\ + golang.org/x/tools/gopls@latest \ + honnef.co/go/tools/cmd/staticcheck@latest \ + golang.org/x/lint/golint@latest \ + github.com/mgechev/revive@latest \ + github.com/go-delve/delve/cmd/dlv@latest \ + github.com/fatih/gomodifytags@latest \ + github.com/haya14busa/goplay/cmd/goplay@latest \ + github.com/cweill/gotests/gotests@latest \ + github.com/josharian/impl@latest" + +if [ "${INSTALL_GO_TOOLS}" = "true" ]; then + echo "Installing common Go tools..." + export PATH=${TARGET_GOROOT}/bin:${PATH} + export GOPATH=/tmp/gotools + export GOCACHE="${GOPATH}/cache" + + mkdir -p "${GOPATH}" /usr/local/etc/vscode-dev-containers "${TARGET_GOPATH}/bin" + cd "${GOPATH}" + + # Use go get for versions of go under 1.16 + go_install_command=install + if [[ "1.16" > "$(go version | grep -oP 'go\K[0-9]+\.[0-9]+(\.[0-9]+)?')" ]]; then + export GO111MODULE=on + go_install_command=get + echo "Go version < 1.16, using go get." + fi + + (echo "${GO_TOOLS}" | xargs -n 1 go ${go_install_command} -v )2>&1 | tee -a /usr/local/etc/vscode-dev-containers/go.log + + # Move Go tools into path + if [ -d "${GOPATH}/bin" ]; then + mv "${GOPATH}/bin"/* "${TARGET_GOPATH}/bin/" + fi + + # Note: golangci-lint installation has been removed in this tidy version + # to avoid checksum verification issues that may occur with the golangci-lint installer + + # Remove Go tools temp directory + rm -rf "${GOPATH}" +fi + + +chown -R "${USERNAME}:golang" "${TARGET_GOROOT}" "${TARGET_GOPATH}" +chmod -R g+r+w "${TARGET_GOROOT}" "${TARGET_GOPATH}" +find "${TARGET_GOROOT}" -type d -print0 | xargs -n 1 -0 chmod g+s +find "${TARGET_GOPATH}" -type d -print0 | xargs -n 1 -0 chmod g+s + +# Clean up +clean_up + +echo "Done!" diff --git a/test/agent/scenarios.json b/test/agent/scenarios.json new file mode 100644 index 000000000..a4f95e593 --- /dev/null +++ b/test/agent/scenarios.json @@ -0,0 +1,31 @@ +{ + "install_all": { + "image": "mcr.microsoft.com/devcontainers/base:ubuntu", + "features": { + "agent": { + "claudeCodeVersion": "latest", + "installCodex": true, + "installNode": true, + "installPython": true + } + } + }, + "claude_only": { + "image": "mcr.microsoft.com/devcontainers/base:ubuntu", + "features": { + "agent": { + "claudeCodeVersion": "latest", + "installCodex": false + } + } + }, + "codex_only": { + "image": "mcr.microsoft.com/devcontainers/base:ubuntu", + "features": { + "agent": { + "claudeCodeVersion": "none", + "installCodex": true + } + } + } +} diff --git a/test/agent/test.sh b/test/agent/test.sh new file mode 100755 index 000000000..b5ca82f4e --- /dev/null +++ b/test/agent/test.sh @@ -0,0 +1,28 @@ +#!/bin/bash + +set -e + +# Optional: Import test library +source dev-container-features-test-lib + +echo "Testing AI Agent Tools installation..." + +# Check if helper script is installed +check "agent-tools-info exists" test -f /usr/local/bin/agent-tools-info +check "agent-tools-info is executable" test -x /usr/local/bin/agent-tools-info + +# Check Python packages +check "anthropic python package" python3 -c "import anthropic; print('Anthropic SDK installed')" +check "openai python package" python3 -c "import openai; print('OpenAI SDK installed')" + +# Check if pip packages are available +check "pip list shows anthropic" pip3 list | grep anthropic +check "pip list shows openai" pip3 list | grep openai + +# Run the info command +check "agent-tools-info runs" agent-tools-info + +echo "All tests passed!" + +# Report result +reportResults diff --git a/test/agent/test_codex.sh b/test/agent/test_codex.sh new file mode 100755 index 000000000..bfc919a7f --- /dev/null +++ b/test/agent/test_codex.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +set -e + +# Optional: Import test library +source dev-container-features-test-lib + +echo "Testing Codex CLI installation..." + +# Check if Codex CLI is installed +check "codex command exists" command -v codex + +# Check version (if available) +check "codex runs" codex --version || codex --help + +echo "Codex CLI tests passed!" + +# Report result +reportResults diff --git a/test/go-tidy/scenarios.json b/test/go-tidy/scenarios.json new file mode 100644 index 000000000..b9a50a99b --- /dev/null +++ b/test/go-tidy/scenarios.json @@ -0,0 +1,34 @@ +{ + "install_go_tidy_ubuntu": { + "image": "ubuntu:focal", + "features": { + "go-tidy": { + "version": "latest" + } + } + }, + "install_go_tidy_debian": { + "image": "debian:bullseye", + "features": { + "go-tidy": { + "version": "latest" + } + } + }, + "install_go_tidy_alma-8": { + "image": "almalinux:8", + "features": { + "go-tidy": { + "version": "latest" + } + } + }, + "install_go_tidy_fedora": { + "image": "fedora", + "features": { + "go-tidy": { + "version": "latest" + } + } + } +} diff --git a/test/go-tidy/test.sh b/test/go-tidy/test.sh new file mode 100755 index 000000000..86426ff3c --- /dev/null +++ b/test/go-tidy/test.sh @@ -0,0 +1,31 @@ +#!/bin/bash + +set -e + +# Optional: Import test library +source dev-container-features-test-lib + +# go +check "version" go version + +# revive +check "revive version" revive --version +check "revive is installed at correct path" bash -c "which revive | grep /go/bin/revive" + +# gomodifytags +check "gomodifytags is installed at correct path" bash -c "which gomodifytags | grep /go/bin/gomodifytags" + +# goplay +check "goplay is installed at correct path" bash -c "which goplay | grep /go/bin/goplay" + +# gotests +check "gotests is installed at correct path" bash -c "which gotests | grep /go/bin/gotests" + +# impl +check "impl is installed at correct path" bash -c "which impl | grep /go/bin/impl" + +# Verify golangci-lint is NOT installed (expected for go-tidy) +check "golangci-lint should not be installed" bash -c "! command -v golangci-lint" + +# Report result +reportResults