{"id":37156814,"url":"https://github.com/renan-alm/gh-secrets-migrator","last_synced_at":"2026-04-07T08:02:54.337Z","repository":{"id":321222197,"uuid":"1084975293","full_name":"renan-alm/gh-secrets-migrator","owner":"renan-alm","description":"Extension for GH CLI aiming to assist migrating GitHub Secrets (GHEC to GHEC)","archived":false,"fork":false,"pushed_at":"2026-01-12T16:22:24.000Z","size":238,"stargazers_count":2,"open_issues_count":5,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2026-01-12T18:51:26.715Z","etag":null,"topics":["cli","gh-cli","github-secrets","migrations","secrets-management","tools"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/renan-alm.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-10-28T12:23:40.000Z","updated_at":"2026-01-12T15:29:31.000Z","dependencies_parsed_at":null,"dependency_job_id":"248099d6-2f55-4f6a-b93e-43857dab49ff","html_url":"https://github.com/renan-alm/gh-secrets-migrator","commit_stats":null,"previous_names":["renan-alm/gh-secrets-migrator"],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/renan-alm/gh-secrets-migrator","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/renan-alm%2Fgh-secrets-migrator","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/renan-alm%2Fgh-secrets-migrator/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/renan-alm%2Fgh-secrets-migrator/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/renan-alm%2Fgh-secrets-migrator/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/renan-alm","download_url":"https://codeload.github.com/renan-alm/gh-secrets-migrator/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/renan-alm%2Fgh-secrets-migrator/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28430868,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-14T16:38:47.836Z","status":"ssl_error","status_checked_at":"2026-01-14T16:34:59.695Z","response_time":107,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cli","gh-cli","github-secrets","migrations","secrets-management","tools"],"created_at":"2026-01-14T18:37:23.202Z","updated_at":"2026-04-07T08:02:54.328Z","avatar_url":"https://github.com/renan-alm.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# GitHub Secrets Migrator\n\n[![Tests](https://github.com/renan-alm/gh-secrets-migrator/actions/workflows/test-and-lint.yml/badge.svg)](https://github.com/renan-alm/gh-secrets-migrator/actions/workflows/test-and-lint.yml)\n[![Release](https://github.com/renan-alm/gh-secrets-migrator/actions/workflows/release.yml/badge.svg)](https://github.com/renan-alm/gh-secrets-migrator/actions/workflows/release.yml)\n[![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)\n[![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)\n\nA GitHub CLI extension to migrate GitHub repository secrets from a source repository to a target repository using GitHub Actions workflows. Written in Python and compiled to native binaries using PyInstaller.\n\n## Features\n\n- ✨ Migrates secrets from one GitHub repository to another\n- � Supports organization-to-organization secret migration\n- 🔍 Repository scoping for organization secrets (maintains visibility settings)\n- 🌍 Recreates repository environments in target repository\n- 🔐 Automatically encrypts secrets using GitHub's public key\n- 🤖 Uses GitHub Actions workflow for automated migration\n- 🔄 Supports both explicit PATs or GITHUB_TOKEN environment variable\n- 🌐 Supports custom endpoints for GHEC Data Residency, GHES, and EMU\n- ⏱️ Automatic rate limit monitoring and handling\n- 📝 Comprehensive logging with verbose mode\n- ✅ Validates PAT permissions before starting migration\n- 🧹 Automatic cleanup of temporary secrets\n- 🚀 Available as a GitHub CLI extension with precompiled binaries\n- 🍎 Native support for macOS (Intel and Apple Silicon), Linux, and Windows\n\n## Installation\n\n### Option 1: GitHub CLI Extension (Recommended)\n\nInstall as a GitHub CLI extension for the easiest setup:\n\n```bash\n# Install from GitHub\ngh extension install renan-alm/gh-secrets-migrator\n\n# Use the extension\ngh secrets-migrator --source-org myorg --source-repo myrepo --target-org targetorg --target-repo targetrepo\n```\n\nThe extension comes with precompiled binaries for Linux, macOS, and Windows, so no Python installation is required.\n\n### Option 2: Direct Binary Download\n\nDownload the latest precompiled binary for your platform from the [Releases page](https://github.com/renan-alm/gh-secrets-migrator/releases):\n\n```bash\n# Linux AMD64\ncurl -L https://github.com/renan-alm/gh-secrets-migrator/releases/latest/download/gh-secrets-migrator_v\u003cversion\u003e_linux-amd64 -o gh-secrets-migrator\nchmod +x gh-secrets-migrator\n./gh-secrets-migrator --help\n\n# macOS AMD64 (Intel)\ncurl -L https://github.com/renan-alm/gh-secrets-migrator/releases/latest/download/gh-secrets-migrator_v\u003cversion\u003e_darwin-amd64 -o gh-secrets-migrator\nchmod +x gh-secrets-migrator\n./gh-secrets-migrator --help\n\n# macOS ARM64 (Apple Silicon)\ncurl -L https://github.com/renan-alm/gh-secrets-migrator/releases/latest/download/gh-secrets-migrator_v\u003cversion\u003e_darwin-arm64 -o gh-secrets-migrator\nchmod +x gh-secrets-migrator\n./gh-secrets-migrator --help\n\n# Windows (PowerShell)\nInvoke-WebRequest -Uri \"https://github.com/renan-alm/gh-secrets-migrator/releases/latest/download/gh-secrets-migrator_v\u003cversion\u003e_windows-amd64.exe\" -OutFile \"gh-secrets-migrator.exe\"\n.\\gh-secrets-migrator.exe --help\n```\n\n**Note:** Replace `\u003cversion\u003e` with the actual version number (e.g., `1.0.0`). The filenames include the `v` prefix.\n\n### Option 3: From Source (Python)\n\nIf you prefer to run from source or need to make modifications:\n\n#### Prerequisites\n\n- Python 3.10 or higher (Python 3.8 and 3.9 are no longer supported as of v0.3.0)\n- GitHub Personal Access Tokens (PAT) with appropriate scopes (see [Permissions](#permissions) section)\n\n#### Setup\n\n```bash\n# Clone the repository\ngit clone https://github.com/renan-alm/gh-secrets-migrator.git\ncd gh-secrets-migrator\n\n# Install dependencies\npip install -r requirements.txt\n\n# Run from source\npython main.py --help\n\n# Or build a local binary\nmake build\n./bin/gh-secrets-migrator --help\n```\n\n### Docker Setup (Lightweight)\n\nRun the application in a Docker container without installing dependencies locally:\n\n**Build the image:**\n\n```bash\ndocker build -t gh-secrets-migrator .\n```\n\n**Run with Docker:**\n\n```bash\ndocker run --rm \\\n  -e GITHUB_TOKEN=\u003cyour-token\u003e \\\n  gh-secrets-migrator \\\n  --source-org myorg \\\n  --source-repo .github \\\n  --target-org targetorg \\\n  --org-to-org \\\n  --verbose\n```\n\n**Or with explicit PATs:**\n\n```bash\ndocker run --rm \\\n  gh-secrets-migrator \\\n  --source-org myorg \\\n  --source-repo .github \\\n  --target-org targetorg \\\n  --source-pat \u003csource-pat\u003e \\\n  --target-pat \u003ctarget-pat\u003e \\\n  --org-to-org\n```\n\n**Using Docker Compose:**\n\n```bash\n# Set your token in environment\nexport GITHUB_TOKEN=\u003cyour-token\u003e\n\n# Run the migration\ndocker-compose run --rm secrets-migrator \\\n  --source-org myorg \\\n  --source-repo .github \\\n  --target-org targetorg \\\n  --org-to-org\n```\n\n**Image Size:** ~200MB (lightweight Python 3.11 slim base)\n\n### Push to GitHub Container Registry (GHCR)\n\n**Authenticate with GHCR:**\n\n```bash\necho $GITHUB_TOKEN | docker login ghcr.io -u \u003cusername\u003e --password-stdin\n```\n\n**Tag and push the image:**\n\n```bash\n# Build with GHCR tag\ndocker build -t ghcr.io/renan-alm/gh-secrets-migrator:latest .\n\n# Push to GHCR\ndocker push ghcr.io/renan-alm/gh-secrets-migrator:latest\n```\n\n**Run from GHCR:**\n\n```bash\ndocker run --rm \\\n  -e GITHUB_TOKEN=\u003cyour-token\u003e \\\n  ghcr.io/renan-alm/gh-secrets-migrator:latest \\\n  --source-org myorg \\\n  --source-repo .github \\\n  --target-org targetorg \\\n  --org-to-org\n```\n\n**Update docker-compose.yml to use GHCR:**\n\n```yaml\nservices:\n  secrets-migrator:\n    image: ghcr.io/renan-alm/gh-secrets-migrator:latest\n    # ... rest of config\n```\n\n### Automated Publishing (CI/CD)\n\nThe repository includes GitHub Actions workflows that automatically publish Docker images to GHCR:\n\n- **On successful release**: When the Release workflow completes successfully (triggered by pushing a `v*` tag), the Docker image is automatically built and pushed\n- **Pull requests to master**: Docker images are built (but not pushed) to validate the Dockerfile\n\n**Release workflow:**\n\n1. Push a version tag: `git tag v1.2.3 \u0026\u0026 git push origin v1.2.3`\n2. Release workflow validates changelog entry exists and tests passed\n3. Builds binaries for Windows, macOS, and Linux\n4. Creates GitHub Release with artifacts and SHA256 checksums\n5. On success, triggers Docker publish to `ghcr.io/renan-alm/gh-secrets-migrator:v1.2.3`\n\n**No manual steps needed**—just push a tag and the release + Docker image are published!\n\n## Permissions\n\n### Required PAT Scopes\n\nBoth **source** and **target** PATs must have the following scopes:\n\n#### Source PAT Scopes\n\nFor reading source repo and managing temporary secrets:\n\n- `repo` - Full control of private repositories\n- `workflow` - Update GitHub Action workflows (for branch/workflow management)\n\n#### Target PAT Scopes\n\nFor creating secrets in target repository:\n\n- `repo` - Full control of private repositories\n\n### Minimal Permissions Checklist\n\n**Source PAT:**\n\n- ✅ Read repository secrets\n- ✅ Create/update repository secrets (temporary PAT storage)\n- ✅ Delete repository secrets (cleanup)\n- ✅ Create/delete branches\n- ✅ Push to repository\n\n**Target PAT:**\n\n- ✅ Create/update repository secrets\n\n### Creating a Personal Access Token (Classic)\n\n1. Go to GitHub Settings → Developer settings → Personal access tokens (classic)\n2. Click \"Generate new token (classic)\"\n3. Give it a descriptive name (e.g., \"Secrets Migrator Source\")\n4. Select the required scopes (see above)\n5. Click \"Generate token\"\n6. Copy the token immediately (you won't see it again)\n\n⚠️ **Security Note**: Store these tokens securely. Never commit them to repositories.\n\n## Usage\n\nYou can use this tool in three ways:\n1. **As a GitHub CLI extension** (recommended): `gh secrets-migrator [OPTIONS]`\n2. **As a standalone binary**: `./gh-secrets-migrator [OPTIONS]`\n3. **From Python source**: `python main.py [OPTIONS]`\n\nAll examples below work with any of these methods - just replace the command accordingly.\n\n### Basic Usage with Explicit PATs\n\n```bash\n# As GitHub CLI extension\ngh secrets-migrator \\\n  --source-org \u003csource-org\u003e \\\n  --source-repo \u003csource-repo\u003e \\\n  --target-org \u003ctarget-org\u003e \\\n  --target-repo \u003ctarget-repo\u003e \\\n  --source-pat \u003csource-pat\u003e \\\n  --target-pat \u003ctarget-pat\u003e\n\n# As standalone binary\n./gh-secrets-migrator \\\n  --source-org \u003csource-org\u003e \\\n  --source-repo \u003csource-repo\u003e \\\n  --target-org \u003ctarget-org\u003e \\\n  --target-repo \u003ctarget-repo\u003e \\\n  --source-pat \u003csource-pat\u003e \\\n  --target-pat \u003ctarget-pat\u003e\n\n# From source\npython main.py \\\n  --source-org \u003csource-org\u003e \\\n  --source-repo \u003csource-repo\u003e \\\n  --target-org \u003ctarget-org\u003e \\\n  --target-repo \u003ctarget-repo\u003e \\\n  --source-pat \u003csource-pat\u003e \\\n  --target-pat \u003ctarget-pat\u003e\n```\n\n### Using GITHUB_TOKEN Environment Variable\n\nIf you have a single token with permissions for both source and target:\n\n```bash\nexport GITHUB_TOKEN=\u003cyour-token\u003e\n\n# Any of these will work\ngh secrets-migrator \\\n  --source-org \u003csource-org\u003e \\\n  --source-repo \u003csource-repo\u003e \\\n  --target-org \u003ctarget-org\u003e \\\n  --target-repo \u003ctarget-repo\u003e\n```\n\n### Organization-to-Organization Migration (Org Secrets Only)\n\nTo migrate only organization-level secrets (ignoring repository and environment secrets):\n\n```bash\ngh secrets-migrator \\\n  --source-org \u003csource-org\u003e \\\n  --source-repo \u003csource-repo\u003e \\\n  --target-org \u003ctarget-org\u003e \\\n  --source-pat \u003csource-pat\u003e \\\n  --target-pat \u003ctarget-pat\u003e \\\n  --org-to-org\n```\n\n**Note:**\n\n- Source repository is **required** to host the migration workflow\n- Target repository is optional; if not provided, defaults to the same name as source repo\n- Only organization-level secrets are migrated; repository and environment secrets are ignored\n\n**Example:**\n\n```bash\ngh secrets-migrator \\\n  --source-org myorg \\\n  --source-repo .github \\\n  --target-org targetorg \\\n  --org-to-org \\\n  --verbose\n```\n\n**With explicit target repository:**\n\n```bash\ngh secrets-migrator \\\n  --source-org myorg \\\n  --source-repo .github \\\n  --target-org targetorg \\\n  --target-repo .github \\\n  --org-to-org \\\n  --verbose\n```\n\n### With Verbose Logging\n\n```bash\ngh secrets-migrator \\\n  --source-org myorg \\\n  --source-repo source-repo \\\n  --target-org targetorg \\\n  --target-repo target-repo \\\n  --source-pat \u003csource-pat\u003e \\\n  --target-pat \u003ctarget-pat\u003e \\\n  --verbose\n```\n\n### Skipping Environment Recreation\n\nBy default, environments from the source repository are recreated in the target repository. To skip this:\n\n```bash\ngh secrets-migrator \\\n  --source-org \u003csource-org\u003e \\\n  --source-repo \u003csource-repo\u003e \\\n  --target-org \u003ctarget-org\u003e \\\n  --target-repo \u003ctarget-repo\u003e \\\n  --source-pat \u003csource-pat\u003e \\\n  --target-pat \u003ctarget-pat\u003e \\\n  --skip-envs\n```\n\n### Example\n\n```bash\ngh secrets-migrator \\\n  --source-org renan-org \\\n  --source-repo .github \\\n  --target-org demo-org-renan \\\n  --target-repo migration-sample \\\n  --verbose\n```\n\n## How It Works\n\n1. **Validates PAT permissions** - Checks both PATs have necessary scopes before proceeding\n2. **Monitors rate limits** - Continuously checks GitHub API rate limits and automatically waits if critically low (\u003c 100 calls remaining)\n3. **Recreates environments** (unless `--skip-envs` is set) - Creates environments from source repo in target repo:\n   - Lists all environments from source repository\n   - Creates each environment in target repository\n   - Gracefully skips if environment already exists (idempotent)\n4. **Lists secrets** - Gets all secrets from source repo (repo, environment, and org secrets for logging)\n5. **Creates temporary secrets** - Stores both PATs in source repo:\n   - `SECRETS_MIGRATOR_TARGET_PAT` (encrypted) - Used by workflow to access target repo\n   - `SECRETS_MIGRATOR_SOURCE_PAT` (encrypted) - Used by workflow cleanup to delete temporary secrets\n6. **Creates migration branch** - Creates a new branch called `migrate-secrets`\n7. **Pushes workflow** - Commits GitHub Actions workflow to migration branch\n8. **Workflow runs** - Triggered by push to `migrate-secrets` branch:\n   - Reads all secrets from source repo\n   - Filters out system secrets (`SECRETS_MIGRATOR_*`, `github_token`)\n   - For each remaining secret: creates it in target repo/environment/org using target PAT\n   - Maintains organization secret visibility and repository scoping\n   - Cleanup (always runs):\n     - Deletes `SECRETS_MIGRATOR_TARGET_PAT` from source repo\n     - Deletes `SECRETS_MIGRATOR_SOURCE_PAT` from source repo\n     - Deletes the migration branch\n\n## Makefile Commands\n\n```bash\nmake install       # Install dependencies\nmake dev          # Install with dev dependencies (includes linters/testing)\nmake lint         # Run linting checks (flake8 + pylint)\nmake format       # Format code with black\nmake test         # Run tests with pytest\nmake clean        # Clean build artifacts, cache, .pyc files\nmake help         # Show all available commands\n```\n\n## Configuration\n\n### Required Flags\n\n- `--source-org`: Source organization name\n- `--source-repo`: Source repository name (**always required** - migration workflow runs in this repository)\n- `--target-org`: Target organization name\n\n### Conditionally Required Flags\n\n- `--target-repo`: Target repository name (required for repo-to-repo migration; optional for org-to-org, defaults to source-repo name if not provided)\n\n### Optional Flags\n\n- `--source-pat`: Source PAT (required if GITHUB_TOKEN not set)\n- `--target-pat`: Target PAT (required if GITHUB_TOKEN not set)\n- `--verbose`: Enable verbose logging (shows debug messages)\n- `--skip-envs`: Skip environment recreation (by default environments are recreated)\n- `--org-to-org`: Migrate only organization-level secrets (requires `--org-to-org` flag, ignores repo and env secrets)\n- `--source-endpoint`: GitHub API endpoint for source (default: `https://api.github.com`)\n- `--target-endpoint`: GitHub API endpoint for target (default: `https://api.github.com`)\n\n### Environment Variables\n\nAll CLI flags can also be set via environment variables:\n\n**Authentication:**\n- `GITHUB_TOKEN`: If set, uses this token for both source and target authentication (must have permissions for both repos)\n- `SOURCE_PAT`: Personal Access Token for source repository (overrides GITHUB_TOKEN if set)\n- `TARGET_PAT`: Personal Access Token for target repository (overrides GITHUB_TOKEN if set)\n\n**Repository Configuration:**\n- `SOURCE_ORG`: Source organization name\n- `SOURCE_REPO`: Source repository name\n- `TARGET_ORG`: Target organization name\n- `TARGET_REPO`: Target repository name\n\n**Endpoints:**\n- `SOURCE_ENDPOINT`: GitHub API endpoint for source organization/repository (default: https://api.github.com)\n- `TARGET_ENDPOINT`: GitHub API endpoint for target organization/repository (default: https://api.github.com)\n\n**Options:**\n- `VERBOSE`: Enable verbose logging (set to any non-empty value)\n- `SKIP_ENVS`: Skip environment recreation (set to any non-empty value)\n- `ORG_TO_ORG`: Migrate only organization-level secrets (set to any non-empty value)\n\n### Custom Endpoints (GHEC Data Residency, GHES, EMU)\n\nThe tool supports custom GitHub API endpoints for:\n- **GHEC Data Residency**: GitHub Enterprise Cloud with data residency requirements\n- **GHEC EMU**: GitHub Enterprise Cloud with Enterprise Managed Users\n- **GHES**: GitHub Enterprise Server (self-hosted)\n\n#### Endpoint URL Formats\n\n- **Standard GitHub.com**: `https://api.github.com` (default)\n- **GHEC Data Residency**: `https://api.\u003cINSTANCE\u003e.ghe.com` (where `\u003cINSTANCE\u003e` is your organization's instance name)\n- **GitHub Enterprise Server**: `https://github.example.com/api/v3`\n\n**Data Residency Example**: If your instance is `nicklegan`, use `https://api.nicklegan.ghe.com`\n\n#### Examples\n\n**Migrate from GitHub.com to GHEC Data Residency:**\n```bash\ngh secrets-migrator \\\n  --source-org myorg \\\n  --source-repo myrepo \\\n  --target-org targetorg \\\n  --target-repo targetrepo \\\n  --target-endpoint https://api.yourinstance.ghe.com\n```\n\n**Migrate from GHEC Data Residency to GitHub.com:**\n```bash\ngh secrets-migrator \\\n  --source-org myorg \\\n  --source-repo myrepo \\\n  --target-org targetorg \\\n  --target-repo targetrepo \\\n  --source-endpoint https://api.yourinstance.ghe.com\n```\n\n**Migrate between different GHEC Data Residency instances:**\n```bash\ngh secrets-migrator \\\n  --source-org myorg \\\n  --source-repo myrepo \\\n  --target-org targetorg \\\n  --target-repo targetrepo \\\n  --source-endpoint https://api.sourceinstance.ghe.com \\\n  --target-endpoint https://api.targetinstance.ghe.com\n```\n\n**Migrate from GitHub Enterprise Server:**\n```bash\ngh secrets-migrator \\\n  --source-org myorg \\\n  --source-repo myrepo \\\n  --target-org targetorg \\\n  --target-repo targetrepo \\\n  --source-endpoint https://github.example.com/api/v3\n```\n\n**Using environment variables:**\n```bash\nexport SOURCE_ENDPOINT=https://api.sourceinstance.ghe.com\nexport TARGET_ENDPOINT=https://api.targetinstance.ghe.com\n\ngh secrets-migrator \\\n  --source-org myorg \\\n  --source-repo myrepo \\\n  --target-org targetorg \\\n  --target-repo targetrepo\n```\n\n**Organization-to-Organization migration with custom endpoints:**\n```bash\ngh secrets-migrator \\\n  --source-org myorg \\\n  --source-repo .github \\\n  --target-org targetorg \\\n  --source-endpoint https://api.sourceinstance.ghe.com \\\n  --target-endpoint https://api.targetinstance.ghe.com \\\n  --org-to-org\n```\n\n**Note**: GHEC EMU (Enterprise Managed Users) can use either standard GitHub.com endpoints (`https://api.github.com`) or Data Residency endpoints (`https://api.\u003cINSTANCE\u003e.ghe.com`), depending on your organization's configuration.\n\n## Security\n\n### ✅ What's Secure\n\n- Secrets are **encrypted at rest** in GitHub using libsodium sealed boxes\n- Only available to workflows via `${{ secrets.* }}` context\n- Secrets are **masked in GitHub Actions logs** (redacted automatically)\n- Temporary `SECRETS_MIGRATOR_TARGET_PAT` and `SECRETS_MIGRATOR_SOURCE_PAT` are **always cleaned up** after workflow completes\n- Cleanup runs even if migration fails (`if: always()` condition)\n- Workflow cleanup deletes the migration branch automatically\n\n### ⚠️ Security Notes\n\n- PATs should be treated like passwords - keep them secret\n- Use separate PATs for source and target for better access control\n- Consider using organization-level secrets to rotate credentials\n- Review the generated workflow before running (it's visible in the Actions tab)\n- Tokens are visible to anyone with write access to the source repository (they can read the workflow file)\n\n## Environment Recreation\n\nThe tool automatically recreates all environments from the source repository in the target repository. This is useful for maintaining environment parity between repositories.\n\n### Behavior\n\n- **Default**: Environments are automatically recreated\n- **Graceful**: If an environment already exists in the target (HTTP 409), it is silently skipped\n- **Idempotent**: Safe to run multiple times; existing environments won't cause failures\n- **Optional**: Use `--skip-envs` flag to skip environment recreation\n\n### Example Output\n\n```bash\nℹ️  Recreating environments...\nℹ️  Environments to recreate (3 total):\n  - production\n  - staging\n  - development\n✅ Environment recreation completed!\n```\n\n### Environment-Specific Secrets\n\nEnvironment-specific secrets are now migrated! The tool generates one workflow step per environment-secret combination:\n\n- Lists all environment secrets from the source repository\n- Creates dynamic workflow steps for each secret\n- Each step migrates that specific secret to the target environment\n- Secrets are created using the values already available in the workflow context\n\n## Limitations\n\n- Both source and target PATs must have appropriate scopes\n- Workflow runs on source repository (not target)\n- Cannot migrate action secrets from Dependabot or Codespaces scopes\n- Source and target repositories must be accessible to their respective PATs\n- For org-to-org migration: only organization-level secrets are migrated (repo and environment secrets are excluded)\n\n## Troubleshooting\n\n### \"Invalid PAT credentials or insufficient permissions\"\n\n- Verify your PATs are valid: `curl -H \"Authorization: token \u003cPAT\u003e\" https://api.github.com/user`\n- Check scopes: Go to GitHub Settings → Developer settings → Personal access tokens (classic) → Select token → View scopes\n- Ensure PATs have `repo` and `workflow` scopes\n\n### \"Connection refused\" or Authentication errors\n\n- Verify organization/repository names are correct\n- Check that PATs haven't expired\n- Ensure you have access to both organizations\n\n### Workflow doesn't run\n\n- Check that the migration branch was created: `Settings \u003e Branches`\n- Verify GitHub Actions is enabled in the source repository\n- Check the Actions tab for any workflow errors\n- Ensure the workflow file `.github/workflows/migrate-secrets.yml` was created\n\n### Secrets not appearing in target repo\n\n- Verify target PAT has permission to create secrets in target repo\n- Check that secret names don't start with `SECRETS_MIGRATOR_` (filtered out)\n- Review workflow logs in the Actions tab\n- Verify target repository is accessible to target PAT\n\n### \"Resource not accessible by integration\" error\n\n- This typically means the PAT doesn't have the `repo` or `workflow` scope\n- Update your source PAT to include these scopes\n- Regenerate the PAT if needed\n\n### Temporary secrets not being deleted\n\n- Check workflow cleanup logs in Actions tab\n- Manually delete `SECRETS_MIGRATOR_TARGET_PAT` and `SECRETS_MIGRATOR_SOURCE_PAT` from source repo\n- Verify source PAT has delete permissions\n\n## Development\n\n### Setting Up Development Environment\n\n```bash\n# Clone the repository\ngit clone https://github.com/renan-alm/gh-secrets-migrator.git\ncd gh-secrets-migrator\n\n# Set up development environment\nmake dev\n\n# Run tests\nmake test\n\n# Run linting\nmake lint\n\n# Format code\nmake format\n```\n\n### Building Binaries\n\n```bash\n# Build for current platform\nmake build\n\n# The binary will be in bin/gh-secrets-migrator\n./gh-secrets-migrator --help\n\n# Clean build artifacts\nmake clean\n```\n\n### Testing Changes\n\n```bash\n# Run with verbose logging from source\npython main.py \\\n  --source-org myorg \\\n  --source-repo repo \\\n  --target-org targetorg \\\n  --target-repo target \\\n  --verbose\n\n# Or test the built binary\nmake build\n./gh-secrets-migrator --verbose --help\n```\n\n### Release Process\n\nThe project uses GitHub Actions to automatically build and release binaries for multiple platforms:\n\n1. Update `CHANGELOG.md` with the new version entry\n2. Ensure all tests pass: `make test`\n3. Create and push a version tag:\n   ```bash\n   git tag v1.0.0\n   git push origin v1.0.0\n   ```\n4. GitHub Actions will automatically:\n   - Validate the changelog entry exists\n   - Verify tests passed on master\n   - Build binaries for Linux, macOS, and Windows\n   - Create a GitHub release with binaries\n   - Make the extension installable via `gh extension install`\n\n### Available Make Targets\n\n```bash\nmake help         # Show all available commands\nmake install      # Install dependencies\nmake dev          # Install with dev dependencies\nmake lint         # Run linting checks\nmake format       # Format code with black\nmake test         # Run tests with pytest\nmake build        # Build for current platform\nmake clean        # Clean build artifacts\n```\n\n## API Reference\n\n### CLI Command\n\n```bash\ngh secrets-migrator [OPTIONS]\n# or: ./gh-secrets-migrator [OPTIONS]\n# or: python main.py [OPTIONS]\n\nOptions:\n  --source-org TEXT         Source organization name [required]\n  --source-repo TEXT        Source repository name [required]\n  --target-org TEXT         Target organization name [required]\n  --target-repo TEXT        Target repository name [conditionally required]\n  --source-pat TEXT         Source Personal Access Token (defaults to GITHUB_TOKEN)\n  --target-pat TEXT         Target Personal Access Token (defaults to GITHUB_TOKEN)\n  --source-endpoint TEXT    GitHub API endpoint for source (default: https://api.github.com)\n  --target-endpoint TEXT    GitHub API endpoint for target (default: https://api.github.com)\n  --verbose                 Enable verbose logging\n  --skip-envs               Skip environment recreation\n  --org-to-org              Migrate only organization-level secrets\n  --help                    Show help message\n```\n\n## Dependencies\n\nThe project uses the following dependencies (see `requirements.txt`):\n\n**Core Dependencies:**\n- `PyGithub==2.8.1` - GitHub API client library\n- `Click==8.3.1` - CLI framework for building command-line interfaces\n- `python-dotenv==1.2.1` - Environment variable management from .env files\n\n**Development \u0026 Build Tools:**\n- `PyInstaller==6.18.0` - Binary compilation for multi-platform distribution\n- `pytest==9.0.2` - Testing framework\n- `pytest-cov==7.0.0` - Code coverage reporting\n- `mypy==1.19.1` - Static type checking\n- `flake8==7.3.0` - Python linting\n- `bandit==1.9.3` - Security vulnerability scanner\n\n**Python Version:**\n- Requires Python 3.10 or higher (Python 3.8 and 3.9 support was dropped in v0.3.0)\n\n## License\n\n[LICENSE](LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frenan-alm%2Fgh-secrets-migrator","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frenan-alm%2Fgh-secrets-migrator","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frenan-alm%2Fgh-secrets-migrator/lists"}