GitHub Environment Secrets Management Tool - Automate uploading secrets from .env files to GitHub environments
Project description
🔐 ghvault
GitHub Environment Secrets Management Tool - Automate uploading secrets from .env files to GitHub environments
🚀 Features
- Bulk Upload: Upload multiple secrets from
.envor similar files to GitHub environments - Individual Secret Management: Create, update, and delete individual secrets
- Environment Support: Manage secrets across different GitHub environments (staging, production, etc.)
- Secure Encryption: Uses GitHub's public key encryption for secure secret storage
- Progress Tracking: Visual progress bars for bulk operations
- Token Validation: Automatic GitHub token validation and session management
- Cross-Platform: Works on macOS, Linux, and Windows
📦 Installation
From PyPI (Recommended)
pip install ghvault
From Source
git clone https://github.com/aoamusat/ghvault.git
cd ghvault
pip install -e .
Using uv (Development)
git clone https://github.com/aoamusat/ghvault.git
cd ghvault
uv sync
🔧 Setup
GitHub Token
You need a GitHub Personal Access Token with the following permissions:
repo(Full control of private repositories)admin:repo_hook(Read and write repository hooks)
Option 1: Environment Variable (Recommended)
export GH_TOKEN=your_github_token_here
Option 2: Interactive Prompt
If no token is found in environment variables, ghvault will prompt you to enter it interactively.
Repository Configuration
Ensure your GitHub repository has environments configured:
- Go to your repository → Settings → Environments
- Create environments (e.g.,
staging,production) - Configure environment protection rules as needed
🎯 Usage
Command Overview
ghvault --help
Set Individual Secret
ghvault set <environment> <secret_name> <secret_value> --owner <owner> --repo <repo>
Example:
ghvault set production DATABASE_URL "postgresql://user:pass@host:5432/db" --owner myorg --repo myapp
Bulk Upload from .env File
ghvault bulk <environment> --env-file .env --owner <owner> --repo <repo>
Example:
ghvault bulk staging --env-file .env.staging --owner myorg --repo myapp
List Environment Secrets
ghvault list <environment> --owner <owner> --repo <repo>
Example:
ghvault list production --owner myorg --repo myapp
Delete Individual Secret
ghvault delete <environment> <secret_name> --owner <owner> --repo <repo>
Example:
ghvault delete staging OLD_API_KEY --owner myorg --repo myapp
Bulk Delete Secrets
ghvault delete-bulk <environment> --secrets-file secrets_to_delete.txt --owner <owner> --repo <repo>
Example:
ghvault delete-bulk production --secrets-file cleanup.txt --owner myorg --repo myapp
📁 File Formats
.env File Format
# Database Configuration
DATABASE_URL=postgresql://user:pass@localhost:5432/mydb
DATABASE_PASSWORD=super_secret_password
# API Keys
STRIPE_SECRET_KEY=sk_test_123456789
SENDGRID_API_KEY=SG.abc123def456
# Feature Flags
ENABLE_FEATURE_X=true
DEBUG_MODE=false
Secrets List File Format (for bulk delete)
DATABASE_PASSWORD
OLD_API_KEY
DEPRECATED_SECRET
TEMP_TOKEN
🔒 Security Features
- Encryption: All secrets are encrypted using GitHub's public key before transmission
- Token Validation: Automatic validation of GitHub tokens before operations
- Secure Input: Hidden input for sensitive token entry
- No Local Storage: Tokens are only stored in memory during session
- HTTPS Only: All API communications use HTTPS
🛠️ Development
Prerequisites
- Python 3.10+
- uv (recommended) or pip
Setup Development Environment
git clone https://github.com/aoamusat/ghvault.git
cd ghvault
uv sync
source .venv/bin/activate # On Windows: .venv\Scripts\activate
Run Tests
Quick Start
# Install test dependencies
uv sync --extra test
# Run all tests
pytest
Test Categories
# Run unit tests only
pytest -m "unit or not integration"
# Run integration tests only
pytest -m integration
# Run with coverage report
pytest --cov=ghvault --cov-report=html --cov-report=term-missing
# Run specific test file
pytest tests/test_api.py
# Run specific test class
pytest tests/test_api.py::TestTokenManagement
# Run specific test method
pytest tests/test_api.py::TestTokenManagement::test_set_github_token
Using Make (if available)
# Run all tests
make test
# Run with coverage
make test-cov
# Run unit tests only
make test-unit
# Run integration tests only
make test-integration
# Run fast (stop on first failure)
make test-fast
Using the Test Runner Script
# Run all checks (format, lint, type-check, tests)
python run_tests.py
# Run specific test type
python run_tests.py --type unit
python run_tests.py --type integration
python run_tests.py --type coverage
python run_tests.py --type lint
# Run in verbose mode
python run_tests.py --verbose
# Stop on first failure
python run_tests.py --fast
Test Structure
tests/test_api.py- Tests for API functionality (GitHub API interactions)tests/test_cli.py- Tests for CLI commands and user interfacetests/test_integration.py- End-to-end integration teststests/conftest.py- Shared fixtures and test configuration
Coverage Reports
After running tests with coverage, you can view detailed reports:
# Generate HTML coverage report
pytest --cov=ghvault --cov-report=html
# Open coverage report in browser (macOS)
open htmlcov/index.html
# View coverage in terminal
pytest --cov=ghvault --cov-report=term-missing
Code Quality
# Install development tools
uv add --dev black isort flake8 mypy
# Format code
black .
isort .
# Lint code
flake8 .
mypy .
📊 Dependencies
- click (>=8.2.1): Command-line interface creation
- httpx (>=0.28.1): HTTP client for GitHub API calls
- pynacl (>=1.5.0): Cryptographic library for secret encryption
- tqdm (>=4.67.1): Progress bars for bulk operations
- typer (>=0.16.1): Modern CLI framework
🚀 CI/CD
The project includes GitHub Actions workflows for:
- Automated Testing: Run tests on multiple Python versions
- PyPI Publishing: Automatic publishing to PyPI on tagged releases
- TestPyPI Publishing: Publishing to TestPyPI on every push
Release Process
- Update version in
pyproject.toml - Create and push a git tag:
git tag v0.1.0 git push origin v0.1.0
- GitHub Actions will automatically build and publish to PyPI
🤝 Contributing
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Make your changes
- Add tests for new functionality
- Ensure all tests pass (
pytest) - Format code (
black . && isort .) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
📝 Examples
Complete Workflow Example
# 1. Set up environment
export GH_TOKEN=ghp_xxxxxxxxxxxxxxxxxxxx
# 2. Create .env file
cat > .env.production << EOF
DATABASE_URL=postgresql://prod:secret@db.example.com:5432/app
REDIS_URL=redis://redis.example.com:6379/0
API_SECRET_KEY=prod-secret-key-12345
STRIPE_LIVE_KEY=sk_live_abcdef123456
EOF
# 3. Upload all secrets to production environment
ghvault bulk production --env-file .env.production --owner mycompany --repo myapp
# 4. Verify secrets were uploaded
ghvault list production --owner mycompany --repo myapp
# 5. Update individual secret
ghvault set production API_VERSION "v2.1" --owner mycompany --repo myapp
# 6. Clean up old secrets
echo "OLD_API_KEY" > cleanup.txt
echo "DEPRECATED_TOKEN" >> cleanup.txt
ghvault delete-bulk production --secrets-file cleanup.txt --owner mycompany --repo myapp
Integration with CI/CD
# .github/workflows/deploy.yml
name: Deploy Secrets
on:
push:
branches: [main]
jobs:
deploy-secrets:
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 ghvault
run: pip install ghvault
- name: Deploy secrets to staging
env:
GH_TOKEN: ${{ secrets.GH_TOKEN }}
run: |
ghvault bulk staging --env-file .env.staging --owner ${{ github.repository_owner }} --repo ${{ github.event.repository.name }}
🐛 Troubleshooting
Common Issues
Token Permission Error
❌ Failed to validate token: 403 Forbidden
- Ensure your token has
repoandadmin:repo_hookpermissions - Check that the repository exists and you have access
Environment Not Found
❌ Environment 'production' not found
- Create the environment in GitHub: Repository → Settings → Environments
- Ensure the environment name matches exactly (case-sensitive)
Invalid .env File
❌ Failed to parse .env file
- Check file format:
KEY=value(no spaces around=) - Ensure file exists and is readable
- Avoid quotes unless they're part of the value
Debug Mode
Set environment variable for verbose output:
export GHVAULT_DEBUG=1
ghvault bulk production --env-file .env
📄 License
This project is licensed under the MIT License - see the LICENSE file for details.
🙏 Acknowledgments
- GitHub API for providing robust secrets management
- The Python community for excellent libraries
- Contributors and users who help improve this tool
📞 Support
- Issues: GitHub Issues
- Discussions: GitHub Discussions
- Documentation: Wiki
Made with ❤️ for the GitHub community
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file ghvault-1.0.0.tar.gz.
File metadata
- Download URL: ghvault-1.0.0.tar.gz
- Upload date:
- Size: 61.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
061361506c5c67c4fff8beabe2162f9e58f9fac405313da7eada2fa6dfdd8378
|
|
| MD5 |
1d7c89a3e674ac92135c8e49f334d773
|
|
| BLAKE2b-256 |
766997f4f88577c2f72f6ad4ee769dd4223dfb8fd2551afed9f6a689fcb9a8b7
|
Provenance
The following attestation bundles were made for ghvault-1.0.0.tar.gz:
Publisher:
publish.yaml on aoamusat/ghvault
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ghvault-1.0.0.tar.gz -
Subject digest:
061361506c5c67c4fff8beabe2162f9e58f9fac405313da7eada2fa6dfdd8378 - Sigstore transparency entry: 433563542
- Sigstore integration time:
-
Permalink:
aoamusat/ghvault@afecb3515e73d7779614f6b3ef1ea03a4c9b7a9d -
Branch / Tag:
refs/tags/v1.0.0 - Owner: https://github.com/aoamusat
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yaml@afecb3515e73d7779614f6b3ef1ea03a4c9b7a9d -
Trigger Event:
push
-
Statement type:
File details
Details for the file ghvault-1.0.0-py3-none-any.whl.
File metadata
- Download URL: ghvault-1.0.0-py3-none-any.whl
- Upload date:
- Size: 11.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e16390bdad969dad8689a328c0911e499c26bc2b859957dc2bfdcf29a0d9eb54
|
|
| MD5 |
0aa479b7384603ff227ea9631d7ad156
|
|
| BLAKE2b-256 |
f2601f5626bcdcd492fa77f9e91c5dd86d3a47c770fedca48376674f96e793dd
|
Provenance
The following attestation bundles were made for ghvault-1.0.0-py3-none-any.whl:
Publisher:
publish.yaml on aoamusat/ghvault
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ghvault-1.0.0-py3-none-any.whl -
Subject digest:
e16390bdad969dad8689a328c0911e499c26bc2b859957dc2bfdcf29a0d9eb54 - Sigstore transparency entry: 433563556
- Sigstore integration time:
-
Permalink:
aoamusat/ghvault@afecb3515e73d7779614f6b3ef1ea03a4c9b7a9d -
Branch / Tag:
refs/tags/v1.0.0 - Owner: https://github.com/aoamusat
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yaml@afecb3515e73d7779614f6b3ef1ea03a4c9b7a9d -
Trigger Event:
push
-
Statement type: