Skip to content

mona-actions/gh-self-serve-migration

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

4 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

πŸš€ GitHub Self-Service Migration Framework

Empower Your Users to Migrate Their Own Repositories

Self-Service GEI Powered Enterprise Ready

Free your admins from migration bottlenecks. This framework enables GitHub users to migrate their own repositories through a guided, self-service workflowβ€”while maintaining enterprise-grade security, access controls, and governance.

Built on GitHub Enterprise Importer (GEI) with intelligent batching, parallel processing, and comprehensive asset transfer. Supports GHES-to-GHEC, GHEC-to-GHEC, and EMU migrations with role-based access control, dry-run validation, and automatic feature detection.


πŸ“š Documentation

Document Description
πŸ“– README.md Framework overview, features, and architecture (you are here)
πŸ› οΈ SETUP.md Complete setup guide from prerequisites to first migration
πŸš€ USAGE.md How to run migrations with the 4-step guided workflow
πŸ”„ UPDATING.md Keep your framework up-to-date with latest features

New to this framework? Start with SETUP.md for step-by-step installation and configuration.

Ready to migrate? See USAGE.md for the complete migration workflow.

Using this as a template? See UPDATING.md for how to receive framework updates while preserving your configuration.


✨ Key Features

πŸ™‹ Self-Service Empowerment

Feature Description Benefit
πŸ“ Issue-Driven Workflow Users create migration requests through GitHub Issues No admin bottleneckβ€”users own their migrations
πŸ” Role-Based Access Control Fine-grained permissions per organization via instances.json Users only see what they're authorized to migrate
πŸ§ͺ Safe Testing Dry-run mode validates before production Users can test migrations risk-free
πŸ“Š Real-Time Visibility Progress updates posted automatically to issues Complete transparencyβ€”no "black box" migrations

βš™οΈ Enterprise-Grade Automation

Feature Description
🏒 Multi-Instance Architecture Support for multiple GHES and GHEC instances with centralized configuration
πŸ“¦ Smart Batching Automatically splits large migrations into 250-repo chunks (configurable to 256 max)
⚑ Parallel Execution Up to 10 concurrent repository migrations per batch (GEI limit)
πŸ”„ Sequential Processing Reliable batch-by-batch execution with comprehensive progress tracking
πŸ’Ύ Complete Data Transfer Git history, LFS, packages, releases, secrets, variables, and environments
πŸ”’ Production Mode Secure migration with automatic source repository locking
πŸ‘₯ User Mapping Automatic mannequin-to-user account mapping with CSV support
🧹 Cleanup Tools Commands for dry-run cleanup and repository deletion
πŸ›‘ Cancellation Support Stop migrations gracefully with /cancel-migration command

🎯 Quick Start

First time setup? Follow the complete SETUP.md guide for detailed installation instructions.

πŸ“‹ Prerequisites

For Admins (One-Time Setup):

  • βœ… GitHub Enterprise Cloud organization (target)
  • βœ… Admin access to source and target instances
  • βœ… Self-hosted GitHub Actions runners (Ubuntu 24.04, 16+ cores, 32GB+ RAM)
  • βœ… Cloud storage (Azure Blob or AWS S3) for GEI backend
  • βœ… Configure instances.json with organizations and authorized users

For Users (Self-Service Migration):

  • βœ… GitHub account with access granted in instances.json
  • βœ… Knowledge of which repositories to migrate
  • βœ… Ability to create issues in the migration framework repository

πŸ“– Complete setup guide: See SETUP.md for step-by-step installation instructions.

πŸš€ Running Migrations

Once your framework is set up, users can migrate their own repositories using the 4-step guided workflow:

  1. Create Migration Issue - Select source and target instances
  2. Select Organizations - Choose which orgs to migrate between
  3. Provide Repository URLs - List the repositories to migrate
  4. Execute Migration - Run dry-run test or production migration

The system provides real-time progress updates and handles batching, parallelization, and feature detection automaticallyβ€”no admin intervention required.

πŸ“– Complete migration guide: See USAGE.md for detailed instructions.

Quick Commands

Command Purpose
/run-dry-run-migration Test migration safely (recommended first)
/run-production-migration Execute production migration
/delete-dry-run Clean up dry-run test repositories
/cancel-migration Stop an ongoing migration

πŸ—οΈ Architecture

πŸ–₯️ Actions Runner Setup

CRITICAL: This system requires self-hosted GitHub Actions runners for batch processing. GitHub-hosted runners are only used for orchestration.

Runner Architecture Overview

The migration system uses a three-tier runner architecture:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Tier 1: Orchestration (GitHub-hosted)              β”‚
β”‚  β”œβ”€ Creates batches                                 β”‚
β”‚  β”œβ”€ Dispatches workflows                            β”‚
β”‚  └─ Posts progress updates                          β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                        ↓
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Tier 2: Batch Processing (Self-hosted)             β”‚
β”‚  β”œβ”€ Runs GEI migrations (10 parallel)               β”‚
β”‚  β”œβ”€ Detects features (LFS, packages, etc.)          β”‚
β”‚  └─ Triggers feature migrations                     β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                        ↓
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Tier 3: Feature Migrations (Self-hosted)           β”‚
β”‚  β”œβ”€ LFS data transfer                               β”‚
β”‚  β”œβ”€ Packages migration                              β”‚
β”‚  β”œβ”€ Releases migration                              β”‚
β”‚  β”œβ”€ Secrets/Variables migration                     β”‚
β”‚  └─ Environments migration                          β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Runner Requirements

Component Runner Type Quantity Purpose
Orchestrator Self-hosted 1 Batch creation, sequencing, progress updates
Batch Processing Self-hosted 1-10 GEI repository migrations (10 for max concurrency)
Feature Migrations Self-hosted 0-10+ LFS, packages, releases, environments

Total Runners for Full Concurrency: 11 (1 orchestrator + 10 batch processors)

Deployment Options

Option 1: Single Server (Recommended for most)

Server: Ubuntu 24.04, 32 cores, 64GB RAM, 1TB SSD
└─ 11 runner processes (all labeled: self-hosted)
   β”œβ”€ 1 runner: Orchestration
   β”œβ”€ 10 runners: Batch processing (max concurrency)
   └─ Shared /opt/migration cache directory

πŸ“– For complete runner installation instructions: See SETUP.md - Set Up Self-Hosted Runners

πŸ’Ύ Storage & Caching

GEI Storage Backend (Required)

GEI requires cloud storage for temporary migration data:

Backend Configuration When to Use
Azure Blob AZURE_STORAGE_CONNECTION_STRING secret Azure infrastructure
AWS S3 AWS_ACCESS_KEY_ID + AWS_SECRET_ACCESS_KEY secrets
AWS_REGION + AWS_BUCKET_NAME variables
AWS infrastructure

Local Cache Directory (Optional but Recommended)

Location: /opt/migration (configurable via LOCAL_CACHE_DIR variable)

Purpose: Speeds up feature detection and migration by caching:

  • Package metadata
  • Release data
  • Environment configurations
  • LFS detection results

Who needs it: Self-hosted runners running batch processing and feature migrations

πŸ“– For complete storage setup: See SETUP.md - Configure Storage Backend

πŸŽ›οΈ Advanced Configuration

βš™οΈ Batch Size Configuration

Control how many repositories are processed per batch:

Location: .github/workflows/trigger.yml

with:
  BATCH_SIZE: 250  # Default: 250, Max: 256 (GitHub Actions matrix limit)

Considerations:

  • Larger batches = fewer sequential batch executions
  • Maximum is 256 (GitHub Actions matrix size limit)
  • Batch size doesn't affect parallelism (always max 10 concurrent per batch)

πŸ”„ Parallel Processing Configuration

Control concurrent migrations per batch:

Location: .github/workflows/batch-processor.yml

strategy:
  max-parallel: 10  # GEI hard limit, do not exceed

Important:

  • ⚠️ Do not exceed 10 - this is a GEI CLI limitation
  • Each parallel job migrates one repository
  • With 10 runners + max-parallel: 10, you get full parallelization

⏱️ Timeout Configuration

For large repositories or slow networks:

Location: .github/workflows/batch-processor.yml

timeout-minutes: 50400  # 35 days (GitHub Actions maximum)

Guidelines:

  • Default: 50400 minutes (35 days)
  • Adjust based on largest repository size
  • Includes time for LFS transfers and feature migrations

🌐 Multi-Instance Configuration

Add additional GitHub instances to instances.json:

{
  "sources": {
    "GHES-PROD": {
      "hostname": "ghes-prod.company.com",
      "tokenSecret": "GHES_PROD_TOKEN",
      "orgs": {...}
    },
    "GHES-DEV": {
      "hostname": "ghes-dev.company.com",
      "tokenSecret": "GHES_DEV_TOKEN",
      "orgs": {...}
    },
    "GHEC-ORG1": {
      "hostname": "github.com",
      "tokenSecret": "GHEC_ORG1_TOKEN",
      "orgs": {...}
    }
  },
  "targets": {
    "GHEC-EMU-PROD": {...},
    "GHEC-EMU-TEST": {...}
  }
}

Then update the issue template (.github/ISSUE_TEMPLATE/gei-batch-migrations.yml):

- type: dropdown
  id: source-instance
  attributes:
    label: 🏠 Source Instance
    options:
      - "GHES-PROD"
      - "GHES-DEV"
      - "GHEC-ORG1"

πŸ” Variables & Secrets Migration

Fully automated - no configuration needed!

How it works:

  1. βœ… Batch processor detects repository variables and secrets
  2. βœ… Triggers variables-secrets.yml workflow automatically
  3. βœ… Migrates variables with actual values
  4. ⚠️ Creates placeholder secrets (security requirement)

Post-migration:

  • Review placeholder secrets: https://github.com/TARGET_ORG/REPO/settings/secrets/actions
  • Update with actual values manually
  • Verify variables migrated correctly

Security Note: GitHub API doesn't allow reading secret values, only names. Placeholders must be manually updated.

🌍 Environments Migration

Fully automated - environments are detected and migrated automatically!

What's Migrated:

  • βœ… Environment names and settings
  • βœ… Protection rules (wait timers, required reviewers)
  • βœ… Deployment branch policies
  • βœ… Custom protection rules
  • ⚠️ Environment secrets (as placeholders, like repo secrets)

Prerequisites:

  • Target repositories must exist (βœ… done via main migration)
  • Environments CSV must be in cache (βœ… auto-generated or pre-cached)

Verification: Check migrated environments at: https://github.com/TARGET_ORG/REPO/settings/environments

πŸ“¦ Feature-Specific Configuration

LFS Migration:

  • Detected automatically via .gitattributes file
  • Optional: Pre-populate ${LOCAL_CACHE_DIR}/${ORG}_lfs.csv for tracking
  • Uses gh-migrate-lfs tool

Packages Migration:

  • Detected from cached package exports
  • Requires LOCAL_CACHE_DIR with package CSV files
  • Supports: npm, Maven, NuGet, Docker, RubyGems

Releases Migration:

  • Detected from cached release data in LOCAL_CACHE_DIR
  • Migrates: Release notes, assets, tags
  • Preserves: Draft status, prerelease flags

🎨 Customizing Visibility Logic

Default behavior: Set via issue template dropdown

Advanced: Modify batch-processor.yml for custom logic:

env:
  VISIBILITY: ${{ github.event.client_payload.batch.targetRepositoryVisibility }}

Options:

  • Private - All migrated repos are private
  • Internal - All repos are internal (requires EMU or GHEC)
  • Mirror - Preserves source visibility (custom implementation required)

πŸ› οΈ Troubleshooting

🚨 Common Issues & Solutions

πŸ”΄ Migration Won't Start

Symptoms:

  • No response after posting migration command
  • Workflows don't trigger

Checklist:

  1. βœ… Verify you completed all 4 steps of the guided workflow
  2. βœ… Check PAT permissions (must include repo, admin:org, workflow)
  3. βœ… Ensure secrets names match instances.json tokenSecret values
  4. βœ… Confirm issue has migration and batch labels
  5. βœ… Verify at least 1 self-hosted runner is online
  6. βœ… Check Actions tab for workflow errors

Debug Commands:

# Verify secrets are configured
gh secret list --repo YOUR-ORG/migraction

# Check runner status
# Go to: https://github.com/YOUR-ORG/migraction/settings/actions/runners
🟑 Batch Processing Stops or Times Out

Symptoms:

  • Batch workflow starts but doesn't complete
  • "Job was cancelled" errors

Steps:

  1. Check Actions tab β†’ Select failed workflow β†’ Review logs
  2. Verify runner availability:
    • Go to Settings β†’ Actions β†’ Runners
    • Ensure runners show "Idle" (green) not "Offline" (gray)
  3. Check runner logs on the server:
    # View runner logs
    cat ~/actions-runner-1/_diag/Runner_*.log
  4. Verify GEI CLI is installed:
    gei --version
    # If not found, set INSTALL_PREREQS=true
  5. Check API rate limits:
    gh api rate_limit --token $YOUR_TOKEN
  6. Re-run failed batch from Actions tab
πŸ”΅ Features Not Migrating (LFS/Packages/Releases)

Symptoms:

  • Main migration succeeds but features missing
  • No LFS objects, packages, or releases in target

Diagnosis:

  1. Check if features were detected:

    • Go to Actions β†’ Batch workflow logs
    • Look for "Detect features requiring additional migration steps"
    • Check output: lfs=true, packages=true, releases=true
  2. Verify LOCAL_CACHE_DIR:

    # On runner machine
    ls -la /opt/migration/
    # Should contain: packages/, releases/, *_environments.csv
  3. Check feature workflow triggering:

    • Go to Actions β†’ Filter by workflow (lfs.yml, packages.yml, etc.)
    • Verify workflows were dispatched
  4. Pre-cache data (recommended for large migrations):

    # Use gh-migrate-* tools to pre-populate cache
    gh extension install mona-actions/gh-migrate-releases
    gh migrate-releases export --source-org ORG --cache-dir /opt/migration
🟠 Access Denied or Authorization Errors

Symptoms:

  • "User does not have access" message
  • Organization checkboxes don't appear
  • "Unknown source instance" errors

Resolution:

  1. Verify user is in allowedUsers:

    // Check .github/scripts/config/instances.json
    {
      "sources": {
        "GHES": {
          "orgs": {
            "engineering": {
              "allowedUsers": ["YOUR-USERNAME", "..."]
            }
          }
        }
      }
    }
  2. Validate instances.json:

    node .github/scripts/config/validate.js
   - Instance config: `"tokenSecret": "GHES_PROD_TOKEN"`
   - GitHub Secret: Must be named exactly `GHES_PROD_TOKEN`

</details>

<details>
<summary>🟣 <strong>Secrets/Variables Not Migrating Correctly</strong></summary>

**Expected Behavior:**
- βœ… Variables: Migrate with actual values
- ⚠️ Secrets: Migrate as placeholders (security limitation)

**Why?**
GitHub API doesn't allow reading secret values, only names. This is by design for security.

**Post-Migration Actions:**
1. Go to target repo: `https://github.com/TARGET_ORG/REPO/settings/secrets/actions`
2. Update each placeholder secret with actual value
3. Verify variables at: `https://github.com/TARGET_ORG/REPO/settings/variables/actions`

</details>

<details>
<summary>⚫ <strong>Runner Out of Disk Space</strong></summary>

**Symptoms:**
- "No space left on device" errors
- LFS migrations fail
- Workflows crash unexpectedly

**Resolution:**

1. **Check disk usage:**
   ```bash
   df -h /opt/migration
   du -sh /opt/migration/*
  1. Clean up old migrations:

    # Remove old cache data
    sudo rm -rf /opt/migration/old-migration-*
    
    # Clean up GEI archives
    sudo find /opt/migration -name "*.tar.gz" -mtime +7 -delete
  2. Increase disk space or move cache:

    # Option 1: Mount larger volume to /opt/migration
    
    # Option 2: Change LOCAL_CACHE_DIR variable to new location
    # Settings β†’ Variables β†’ LOCAL_CACHE_DIR β†’ /mnt/large-disk/migration
  3. Stagger migrations: Run smaller batches to reduce concurrent disk usage

πŸ›‘ Emergency Controls

Stop an ongoing migration:

/cancel-migration

Posts comment to issue, workflows must be manually cancelled in Actions tab.

Clean up dry-run repositories:

/delete-dry-run

⚠️ Use carefully - permanently deletes all repositories with dry-run naming patterns.

Delete specific repositories:

/delete-repositories

⚠️ DANGEROUS - Requires confirmation, permanently deletes repositories.

Re-run a specific failed batch:

  1. Go to Actions tab
  2. Find the failed batch workflow (e.g., "Migration Batch 3")
  3. Click "Re-run failed jobs" or "Re-run all jobs"
  4. Monitor progress in issue comments

πŸ“‹ Debugging Checklist

Before opening a support issue, collect:

**Environment:**
- [ ] Number of repositories: ___
- [ ] Source instance: GHES / GHEC / Other
- [ ] Target instance: GHEC / GHEC EMU
- [ ] Number of self-hosted runners: ___
- [ ] Runner OS: Linux / Windows / macOS

**Configuration:**
- [ ] INSTALL_PREREQS value: ___
- [ ] BATCH_SIZE: ___
- [ ] max-parallel: ___
- [ ] Storage backend: Azure / AWS

**Issue Details:**
- [ ] Workflow run URL: ___
- [ ] Error messages: ___
- [ ] Issue number: #___
- [ ] Batch number (if applicable): ___

**Logs:**
- [ ] Attached workflow logs (Actions β†’ Select run β†’ Download logs)
- [ ] Runner logs (if available)

πŸ” Advanced Debugging

Enable verbose logging:

Edit .github/workflows/batch-processor.yml:

- name: Execute GEI migration
  run: |
    # Add --verbose flag
    gei migrate-repo \
      --verbose \
      --github-source-org $SOURCE_ORG \
      ...

Test GEI manually:

# Test single repository migration
gei migrate-repo \
  --github-source-org source-org \
  --source-repo test-repo \
  --github-target-org target-org \
  --target-repo test-repo \
  --github-source-pat $SOURCE_TOKEN \
  --github-target-pat $TARGET_TOKEN

Validate token permissions:

# Test source token
gh auth status --hostname ghes.company.com

# Test target token
gh auth status

# Check org access
gh api /orgs/YOUR-ORG --hostname ghes.company.com

πŸ“Š Architecture & Workflow

System Architecture

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  End Users (Self-Service Interface)                         β”‚
β”‚  β”œβ”€ Create migration issue via GitHub UI                    β”‚
β”‚  β”œβ”€ Select source/target organizations (filtered by access) β”‚
β”‚  β”œβ”€ Provide repository URLs to migrate                      β”‚
β”‚  β”œβ”€ Execute migrations with simple commands                 β”‚
β”‚  └─ Monitor progress in real-time                           β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                           ↓
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Access Control & Validation (instances.json)               β”‚
β”‚  β”œβ”€ Filters organizations by user permissions               β”‚
β”‚  β”œβ”€ Validates source/target configuration                   β”‚
β”‚  β”œβ”€ Ensures repos match selected organization               β”‚
β”‚  └─ Enforces role-based access control                      β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                           ↓
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Orchestrator (GitHub-hosted: ubuntu-latest)                β”‚
β”‚  β”œβ”€ Resolves tokens from instances.json config              β”‚
β”‚  β”œβ”€ Creates batches (250 repos each)                        β”‚
β”‚  β”œβ”€ Dispatches batches sequentially                         β”‚
β”‚  └─ Posts progress updates to issue                         β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                           ↓
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Batch Processor (Self-hosted: 1-10 runners)                β”‚
β”‚  β”œβ”€ Parallel execution (max 10 concurrent)                  β”‚
β”‚  β”œβ”€ GEI repository migration                                β”‚
β”‚  β”œβ”€ Feature detection (LFS, packages, releases, etc.)       β”‚
β”‚  └─ Triggers feature migrations                             β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                           ↓
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Feature Migrations (Self-hosted: 0-10+ runners)            β”‚
β”‚  β”œβ”€ Variables & Secrets β†’ variables-secrets.yml             β”‚
β”‚  β”œβ”€ LFS Objects β†’ lfs.yml                                   β”‚
β”‚  β”œβ”€ Packages β†’ packages.yml                                 β”‚
β”‚  β”œβ”€ Releases β†’ releases.yml                                 β”‚
β”‚  └─ Environments β†’ environments.yml                         β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                           ↓
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Reporting & Completion                                     β”‚
β”‚  β”œβ”€ Batch status updates posted to issue                    β”‚
β”‚  β”œβ”€ Feature migration reports                               β”‚
β”‚  β”œβ”€ Final summary with statistics                           β”‚
β”‚  └─ Next steps and post-migration checklist                 β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Why Self-Service?

For Admins:

  • 🎯 Eliminate bottlenecks: No more fielding individual migration requests
  • ⏰ Save time: Users handle their own migrations
  • πŸ” Maintain control: Define who can migrate what via instances.json
  • πŸ“Š Full visibility: Monitor all migrations from Actions tab
  • πŸ›‘οΈ Security maintained: Token security, access controls, and audit trails

For Users:

  • πŸš€ Move at your pace: Migrate when it's convenient for you
  • πŸ§ͺ Test safely: Dry-run migrations before production
  • πŸ‘€ Complete transparency: Real-time progress in your issue
  • 🎯 Simple process: Guided 4-step workflow
  • πŸ“ Self-documented: Issue comments provide automatic audit trail

Migration Flow Diagram

sequenceDiagram
    autonumber
    participant User
    participant Issue
    participant Access Control
    participant Orchestrator
    participant Batch Processor
    participant GEI
    participant Feature Workflows
    participant Target
    
    User->>Issue: Create issue + select instances
    Issue->>Access Control: Validate user access
    Access Control-->>Issue: Post allowed organizations
    
    User->>Issue: Select orgs via checkboxes
    Issue->>Access Control: Validate selection
    Access Control-->>Issue: Post repo URL instructions
    
    User->>Issue: Post repository URLs
    Issue->>Access Control: Validate URLs
    Access Control-->>Issue: Post migration commands
    
    User->>Issue: /run-dry-run-migration
    Issue->>Orchestrator: Trigger workflow
    
    Orchestrator->>Orchestrator: Resolve tokens (instances.json)
    Orchestrator->>Orchestrator: Create batches (250 each)
    
    loop Each Batch (Sequential)
        Orchestrator->>Batch Processor: Dispatch batch
        
        loop 10 parallel repos
            Batch Processor->>GEI: Migrate repo
            GEI->>Target: Transfer git data
            Target-->>GEI: Complete
            GEI-->>Batch Processor: Success
            
            Batch Processor->>Batch Processor: Detect features
            
            opt Has LFS/Packages/Releases/Secrets
                Batch Processor->>Feature Workflows: Trigger migration
                Feature Workflows->>Target: Transfer assets
                Target-->>Feature Workflows: Complete
            end
        end
        
        Batch Processor-->>Orchestrator: Batch complete
        Orchestrator->>Issue: Progress update
    end
    
    Orchestrator-->>Issue: Migration complete
    Issue-->>User: Summary report
Loading

Key Workflows

Workflow Trigger Runner Type Purpose
prepare.yml Issue opened/edited ubuntu-latest Parse issue, validate access, post org selection
on-checkbox-edit.yml Issue comment created/edited ubuntu-latest Detect org selection, post Step 3
parse-repos.yml Issue comment ubuntu-latest Parse repository URLs, post Step 4
trigger.yml Issue comment (/run-*-migration) ubuntu-latest Validate setup, trigger orchestrator
orchestrator.yml Called by trigger.yml ubuntu-latest Create batches, dispatch sequentially
batch-processor.yml Repository dispatch self-hosted Run GEI migrations (10 parallel)
variables-secrets.yml Workflow dispatch self-hosted Migrate repo variables & secrets
lfs.yml Workflow dispatch self-hosted Migrate LFS objects
packages.yml Workflow dispatch self-hosted Migrate packages
releases.yml Workflow dispatch self-hosted Migrate releases
environments.yml Workflow dispatch self-hosted Migrate deployment environments
delete.yml Issue comment (/delete-*) ubuntu-latest Cleanup workflows

Data Flow

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  instances.json  β”‚ ← Configuration: instances, orgs, users, tokens
β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
         β”‚
         ↓
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  GitHub Secrets  β”‚ ← Tokens: GHES_PROD_TOKEN, GHEC_EMU_TOKEN, etc.
β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
         β”‚
         ↓
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Orchestrator    β”‚ ← Resolves tokens, creates batches
β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
         β”‚
         ↓
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Batch Dispatch  β”‚ ← repository_dispatch event with payload
β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
         β”‚
         ↓
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Batch Processor  β”‚ ← Receives: repos, tokens, org names
β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
         β”‚
         β”œβ”€β”€β†’ GEI CLI ──→ Cloud Storage (Azure/S3) ──→ Target Repo
         β”‚
         └──→ Feature Detection ──→ LOCAL_CACHE_DIR (/opt/migration)
                  β”‚
                  └──→ Trigger Feature Workflows

Performance Metrics

⚠️ Important: These are general guidelines only. Actual migration times vary greatly based on repository size, complexity, commit history, LFS data, and network conditions. Always run a dry-run migration first to get accurate timing for your specific repositories.

Scale Repositories Batches Est. Time Workers
Small 1-50 1 5-50 min 10 parallel
Medium 51-250 1 50-250 min 10 parallel
Large 251-1000 4 4-17 hours 10 parallel
Enterprise 1001-5000 20 17-84 hours 10 parallel

Time Calculation:

Total Time = (Total Repos Γ· 10 parallel) Γ— Avg Time per Batch
           + (Number of Batches Γ— Batch Overhead)

Where:
- 10 repos in parallel: ~10 minutes (average-sized repos)
- Avg per repo: ~1 minute when running 10 parallel
- Batch Overhead: ~2 minutes per batch
- Large/complex repos: May take significantly longer
- LFS repos: Add 30+ minutes depending on data volume

Factors Affecting Performance:

  • πŸ”’ Repository size and complexity
  • πŸ’Ύ LFS data volume
  • πŸ“¦ Number of packages
  • 🌐 Network bandwidth
  • πŸ–₯️ Runner specifications
  • ⚑ Whether data is pre-cached

πŸ”’ Security Best Practices

πŸ›‘οΈ Token Security

PAT Management:

  • πŸ”‘ Store all tokens in GitHub Secrets (never in code or config files)
  • πŸ”„ Rotate tokens after each major migration
  • πŸ“… Set token expiration dates (recommended: 90 days)
  • 🎯 Use minimum required permissions (see Setup)
  • πŸ‘₯ Create service account tokens (don't use personal tokens)

Required Scopes:

repo                   # Full control of repositories
admin:org              # Full control of orgs and teams  
workflow               # Update GitHub Actions workflows

Token Storage Pattern:

// instances.json references secret names:
"tokenSecret": "GHES_PROD_TOKEN"

// Actual token stored in GitHub Secrets:
Settings β†’ Secrets β†’ Actions β†’ GHES_PROD_TOKEN = "ghp_xxxxxxxxxxxx"

πŸ‘₯ Access Control

Role-Based Access via instances.json:

{
  "sources": {
    "GHES": {
      "orgs": {
        "sensitive-org": {
          "allowedUsers": ["admin1", "admin2"]  // Restricted access
        },
        "general-org": {
          "allowedUsers": ["admin1", "admin2", "dev-team", "migration-team"]
        }
      }
    }
  }
}

Best Practices:

  • πŸ” Limit allowedUsers to minimum necessary personnel
  • πŸ‘₯ Create dedicated migration teams
  • πŸ“‹ Use groups or team names (document mapping separately)
  • πŸ”„ Review access quarterly
  • πŸ“ Audit who has modified instances.json (git history)

During Migration:

  • πŸšͺ Restrict issue creation to authorized teams
  • πŸ”’ Production migrations lock source repositories
  • πŸ‘οΈ Monitor Actions workflow runs
  • πŸ“Š Review migration logs for anomalies

πŸ’Ύ Data Handling

In-Flight Data:

  • πŸ—„οΈ GEI uses cloud storage (Azure/S3) for temporary archives
  • πŸ”’ Ensure storage accounts use encryption at rest
  • 🌐 Use private endpoints for storage (if available)
  • ⏰ Configure lifecycle policies to auto-delete old archives (30 days)

Local Cache (/opt/migration):

  • πŸ“ Contains metadata and feature exports (not full repo data)
  • πŸ” Set appropriate filesystem permissions (755)
  • 🧹 Clean up after successful migrations

### πŸ” Audit & Compliance

**Logging:**
- πŸ“ All migration actions logged in GitHub Actions workflows
- πŸ’¬ Issue comments provide audit trail
- πŸ” Workflow run history retained (default: 90 days)

**Compliance Considerations:**
- πŸ“‹ Document migrations in change management system
- βœ… Obtain necessary approvals before production migrations
- πŸ“Š Generate reports from issue comments and workflow summaries
- πŸ” Ensure GDPR/privacy compliance (user mappings contain PII)

**Audit Checklist:**
```markdown
Pre-Migration:
- [ ] Tokens reviewed and validated
- [ ] Access control verified in instances.json
- [ ] Storage encryption enabled
- [ ] Approvals obtained

During Migration:
- [ ] Monitor workflow runs
- [ ] Review progress updates
- [ ] Check for errors or anomalies

Post-Migration:
- [ ] Verify data integrity
- [ ] Clean up temporary data
- [ ] Rotate tokens
- [ ] Document completion

πŸ“ž Support & Resources

πŸ“š Documentation

πŸ› οΈ Migration Tools

πŸ’¬ Getting Help

Before opening an issue:

  1. πŸ“– Review this README thoroughly
  2. πŸ” Check the Troubleshooting section
  3. πŸ“‹ Review workflow logs in the Actions tab
  4. πŸ§ͺ Test with a small dry-run migration first

🀝 Contributing

Contributions to the framework are welcome! Please:

  • πŸ”€ Fork the repository (for contributions only - users should use templates)
  • 🌿 Create a feature branch
  • βœ… Test your changes thoroughly
  • πŸ“ Update documentation
  • πŸš€ Submit a pull request to the main repository

Note: End users should use "Use this template" rather than forking. See UPDATING.md for details.

πŸ“œ License

This project is provided as-is for use in GitHub migrations. Refer to your organization's policies for usage guidelines.


🎯 Empower Your Users | πŸ”§ Self-Service Migration | πŸ›‘οΈ Enterprise Controls

Made with ❀️ for GitHub Enterprise Migrations

GEI Documentation | GitHub Support | GEI CLI

About

Template for enabling self-service GitHub migrations using IssueOps workflows

Topics

Resources

Stars

Watchers

Forks