Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
name: Deploy and Release

on:
pull_request:
types: [closed]
branches:
- master

jobs:
deploy:
if: github.event.pull_request.merged == true
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v3
with:
fetch-depth: 0

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.11"

- name: Configure Git
run: |
git config --global user.name "GitHub Actions"
git config --global user.email "actions@github.com"

- name: Bump version
run: |
# Make bump_version.py executable
chmod +x bump_version.py

# Run the bump_version script with patch and commit flags
# Use echo to automatically confirm the prompt
echo "y" | python bump_version.py patch --commit

- name: Push changes
run: |
# Get the latest tag
tag=$(git describe --tags --abbrev=0)

# Push both the commit and the tag
git push origin master
git push origin $tag
42 changes: 20 additions & 22 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,31 +2,29 @@ name: Publish to PyPI

on:
push:
branches:
- master
tags:
- 'v*'
- "v*"

jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install build twine
- name: Build and publish
env:
TWINE_USERNAME: "__token__"
TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
run: |
python -m build
twine check dist/*
twine upload dist/*
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.11"

- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install build twine

- name: Build and publish
env:
TWINE_USERNAME: "__token__"
TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
run: |
python -m build
twine check dist/*
twine upload dist/*
112 changes: 102 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,30 +35,85 @@ pipx install infragpt
pip install -e .
```

## Usage
## Credentials Management

### API Keys
InfraGPT requires API keys to work. There are three ways to provide credentials, in order of priority:

InfraGPT requires API keys to work:
### 1. Command Line Parameters

- For OpenAI GPT-4o: Set the `OPENAI_API_KEY` environment variable
- For Anthropic Claude: Set the `ANTHROPIC_API_KEY` environment variable
```bash
# Using OpenAI GPT-4o
infragpt --model gpt4o --api-key "your-openai-api-key" "your prompt here"

You can set these in your shell:
# Using Anthropic Claude
infragpt --model claude --api-key "your-anthropic-api-key" "your prompt here"
```

### 2. Configuration File

InfraGPT stores credentials in `~/.config/infragpt/config.yaml` and uses them automatically on subsequent runs. This file is created:
- When you provide credentials interactively
- Automatically on first run if environment variables are available
- When you use command line parameters

### 3. Environment Variables

Set one or more of these environment variables:

```bash
# For OpenAI GPT-4o
export OPENAI_API_KEY="your-openai-api-key"

# For Anthropic Claude
export ANTHROPIC_API_KEY="your-anthropic-api-key"

# Optionally specify the model
export INFRAGPT_MODEL="gpt4o" # or "claude"
```

### Command Line
**Model Selection Rules**:
- If both API keys are set, InfraGPT uses OpenAI by default unless specified otherwise
- If only one API key is set, the corresponding model is used automatically
- If a model is explicitly selected (via command line or INFRAGPT_MODEL), the corresponding API key must be available

When environment variables are available, InfraGPT will automatically save the detected model and API key to the configuration file for future use.

Run InfraGPT directly with a prompt:
If no credentials are found from any of these sources, if an empty API key is detected, or if an invalid API key is provided, InfraGPT will prompt you to select a model and enter your API key interactively at startup, before accepting any commands.

**API Key Validation:**
- The application validates API keys by making a small test request to the service provider
- When entering credentials interactively, API keys are validated immediately
- Invalid keys from environment variables or the config file are detected at startup
- The system will continue prompting until valid credentials are provided
- All validated credentials are automatically saved to the config file

## Usage

InfraGPT has two main subcommands:
- `generate`: Convert natural language to Google Cloud commands (default command)
- `history`: View or export your command history

### Command Generation

Generate gcloud commands from natural language:

```
infragpt generate "create a new VM instance called test-vm in us-central1 with 2 CPUs"
```

You can also use the tool without specifying the command:

```
infragpt "create a new VM instance called test-vm in us-central1 with 2 CPUs"
```

Or specify the model to use:
Or use the special `--` syntax to handle arguments that might conflict with CLI options:

```
infragpt -- "create a new VM instance called test-vm in us-central1 with 2 CPUs"
```

Specify the model to use:

```
infragpt --model claude "list all my compute instances in europe-west1"
Expand All @@ -72,6 +127,36 @@ Launch InfraGPT in interactive mode (no initial prompt):
infragpt
```

Use keyboard shortcuts in interactive mode:
- `Ctrl+D` to exit the application
- `Ctrl+C` to clear the current input and start a new prompt

### Command History

View your recent command history:

```
infragpt history
```

Limit the number of entries:

```
infragpt history --limit 20
```

Filter by interaction type:

```
infragpt history --type command_execution
```

Export your history to a file:

```
infragpt history --export history.jsonl
```

## Example Commands

- "Create a new GKE cluster with 3 nodes in us-central1"
Expand All @@ -81,9 +166,16 @@ infragpt

## Options

### Generate Command Options
- `--model`, `-m`: Choose the LLM model (gpt4o or claude)
- `--api-key`, `-k`: Provide an API key for the selected model
- `--verbose`, `-v`: Enable verbose output

### History Command Options
- `--limit`, `-l`: Number of history entries to display (default: 10)
- `--type`, `-t`: Filter by interaction type (command_generation, command_action, command_execution)
- `--export`, `-e`: Export history to specified file path

## Contributing

For information on how to contribute to InfraGPT, including development setup, release process, and CI/CD configuration, please see the [CONTRIBUTING.md](CONTRIBUTING.md) file.
For information on how to contribute to InfraGPT, including development setup, release process, and CI/CD configuration, please see the [CONTRIBUTING.md](CONTRIBUTING.md) file.
25 changes: 25 additions & 0 deletions bin/infragpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#!/usr/bin/env python3

import sys
import os

# Find the main infragpt package
try:
import infragpt
except ImportError:
# Add parent directory to path if running from source
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
import infragpt

from infragpt.main import cli, main

if __name__ == "__main__":
# Check if we're using the `--` special form to pass everything after as a prompt
if len(sys.argv) > 1 and sys.argv[1] == "--":
# Special case for "infragpt -- text"
prompt = sys.argv[2:]
sys.argv = [sys.argv[0]] # Reset sys.argv
main(prompt=prompt)
else:
# Normal CLI handling
cli()
4 changes: 2 additions & 2 deletions infragpt/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""InfraGPT - Natural language to Google Cloud commands converter."""

__version__ = "0.1.1"
from .main import main
from .main import cli, main

__all__ = ["main"]
__all__ = ["cli", "main"]
4 changes: 2 additions & 2 deletions infragpt/__main__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#!/usr/bin/env python3
"""Command-line entry point for InfraGPT."""

from .main import main
from .main import cli

if __name__ == "__main__":
main()
cli()
1 change: 1 addition & 0 deletions infragpt/bin/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""Infragpt binary helpers."""
20 changes: 20 additions & 0 deletions infragpt/bin/launcher.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#!/usr/bin/env python3
"""Launcher for InfraGPT."""

import sys
from ..main import cli, main

def main():
"""Entry point for console_scripts."""
# Check if we're using the `--` special form to pass everything after as a prompt
if len(sys.argv) > 1 and sys.argv[1] == "--":
# Special case for "infragpt -- text"
prompt = sys.argv[2:]
sys.argv = [sys.argv[0]] # Reset sys.argv
main(prompt=prompt)
else:
# Normal CLI handling
cli()

if __name__ == "__main__":
main()
Loading