A comprehensive service for migrating repositories from GitLab (self-hosted or cloud) to GitHub, with support for nested repositories, complete metadata migration, and flexible naming strategies.
- Problem: GitLab supports nested groups/subdirectories (e.g.,
group/subgroup/repo), but GitHub doesn't support nested repositories - Solution: Multiple naming strategies to convert nested GitLab paths to flat GitHub repository names:
- Flatten Strategy:
group/subgroup/repo→group-subgroup-repo - Prefix Strategy:
group/subgroup/repo→group-repo - Last Group Strategy: Uses starting group (from config) + repo name. Example: with
group_path: "sikatech/eshop",sikatech/eshop/services/core-service→eshop-core-service - Last Segment Strategy:
group/subgroup/repo→repo - Custom Strategy: Use custom mappings from configuration
- Flatten Strategy:
- ✅ Code Migration: Full git history, branches, and tags
- ✅ Issues: All issues with labels, comments, and metadata
- ✅ Merge Requests: Converted to Pull Requests
- ✅ Labels: All labels with colors and descriptions
- ✅ Milestones: All milestones with due dates
- ✅ Wikis: Wiki pages migration
- ✅ Branches & Tags: All branches and tags preserved
- Configurable migration options (what to migrate)
- Dry-run mode for testing
- Custom name mappings for specific repositories
- Support for both GitLab.com and self-hosted GitLab instances
- Support for GitHub.com and GitHub Enterprise
- Export: Export all GitLab repositories to a JSON file with GitHub name mappings
- Import: Import repository mappings from a JSON file for validation
- Two-Phase Migration: Review and edit mappings before migration
- Manual Review: Edit JSON file to adjust names, skip repositories, or add notes
Copy config.sample.json to config.json and configure:
{
"gitlab": {
"base_url": "https://gitlab.com",
"api_token": "your-gitlab-api-token",
"group_path": "your-group/subgroup"
},
"github": {
"base_url": "https://api.github.com",
"api_token": "your-github-api-token",
"org": "sika365"
},
"migration": {
"naming_strategy": "flatten",
"name_mappings": {
"group/subgroup/old-name": "new-name"
},
"migrate_code": true,
"migrate_issues": true,
"migrate_merge_requests": true,
"migrate_wikis": true,
"migrate_labels": true,
"migrate_milestones": true,
"migrate_branches": true,
"migrate_tags": true,
"preserve_history": true,
"dry_run": false
}
}go run main.go -c config.jsonGET /api/v1/migration/repositoriesGET /api/v1/migration/repositories/:pathPOST /api/v1/migration/repositories/:path/migrate
Content-Type: application/json
{
"dry_run": false
}POST /api/v1/migration/migrate-all
Content-Type: application/json
{
"dry_run": false
}GET /api/v1/migration/status/:pathExports all GitLab repositories to a JSON file with GitHub name mappings. This is useful for reviewing and editing mappings before migration.
POST /api/v1/migration/export
Content-Type: application/json
{
"output_path": "repositories.json"
}Response:
{
"message": "Repositories exported successfully",
"output_path": "repositories.json"
}Imports repository mappings from a JSON file. This validates the file and returns repository count.
POST /api/v1/migration/import
Content-Type: application/json
{
"input_path": "repositories.json"
}Response:
{
"message": "Repositories imported successfully",
"repositories": 150,
"gitlab_group": "sikatech/eshop",
"github_org": "sika365"
}Migrates repositories from a JSON mapping file. This allows you to review and edit the mappings before migration.
POST /api/v1/migration/migrate-from-file
Content-Type: application/json
{
"input_path": "repositories.json",
"dry_run": false
}The migrator supports a two-phase approach for safer and more controlled migrations:
- List Repositories: Discover all repositories in the GitLab group
- Export to JSON: Export all repositories with their GitHub name mappings to a JSON file
- Review and Edit: Review the JSON file and manually adjust any name mappings if needed
- Validate: Ensure all names conform to the GitHub Repository Naming Standard
- Import from JSON: Load the reviewed JSON file
- Migrate from File: Execute migration using the mappings from the JSON file
- Track Status: Monitor migration progress and status
# Step 1: Export repositories to JSON
curl -X POST http://localhost:8080/api/v1/migration/export \
-H "Content-Type: application/json" \
-d '{"output_path": "repositories.json"}'
# Step 2: Review and edit repositories.json if needed
# (Edit the file to adjust GitHub names, mark repositories to skip, etc.)
# Step 3: Import and validate the file
curl -X POST http://localhost:8080/api/v1/migration/import \
-H "Content-Type: application/json" \
-d '{"input_path": "repositories.json"}'
# Step 4: Migrate from the JSON file
curl -X POST http://localhost:8080/api/v1/migration/migrate-from-file \
-H "Content-Type: application/json" \
-d '{"input_path": "repositories.json", "dry_run": false}'The exported JSON file has the following structure:
{
"version": "1.0",
"created_at": "2025-01-15T10:00:00Z",
"updated_at": "2025-01-15T10:00:00Z",
"gitlab_group": "sikatech/eshop",
"github_org": "sika365",
"repositories": [
{
"gitlab_id": 123,
"gitlab_path": "core-service",
"gitlab_full_path": "sikatech/eshop/services/core-service",
"gitlab_name": "core-service",
"gitlab_description": "Core service description",
"gitlab_visibility": "private",
"gitlab_default_branch": "main",
"gitlab_ssh_url": "[email protected]:sikatech/eshop/services/core-service.git",
"gitlab_http_url": "https://gitlab.com/sikatech/eshop/services/core-service.git",
"is_nested": true,
"parent_group_path": "sikatech/eshop/services",
"github_name": "core-service",
"github_description": "Core service description",
"github_private": true,
"migrated": false,
"skip": false
}
]
}You can edit this file to:
- Adjust GitHub repository names to match the naming standard
- Mark repositories to skip (
"skip": true) - Add skip reasons (
"skip_reason": "...") - Review all mappings before migration
This migrator follows the GitHub Repository Naming Standard for Sika365 Microservices Architecture. For complete details, see NAMING_STANDARD.md.
The naming standard defines category-based patterns for different types of repositories:
- Core Services (
/services/):{service-name}-service(e.g.,core-service,auth-service) - Finance Services (
/payment-gateway-adapters/):{gateway-name}-finance-service(e.g.,zarinpal-finance-service,sibank-finance-service) - Connector Services (
/external/):{provider-name}-connector-service(e.g.,darian-connector-service,tipax-connector-service) - CDC Services (
/cdc/):cdc-{agent-name}-agentorcdc-{component-name}(e.g.,cdc-sepidar-agent,cdc-debezium) - Mobile Apps (
/apps/shevi/):mobile-app-{version}or{name}-mobile-app-v3-template(e.g.,mobile-app-v3,market-mobile-app-v3-template) - Admin Panels (
/admin-panels/):admin-panel-{version}(e.g.,admin-panel-v3) - SDKs:
{sdk-name}-sdk-{language}-{version}or{prefix}-sdk(e.g.,eshop-sdk-go-v1,cdc-sdk)
- Use hyphens, not underscores:
core-service✅,core_service❌ - Use lowercase only:
auth-service✅,Auth-Service❌ - Be descriptive but concise:
zarinpal-finance-service✅,zarinpal-payment-gateway-adapter-service❌ - Always use
-servicesuffix for microservices:core-service✅,core❌
For complete naming patterns, examples, and migration mappings, refer to NAMING_STANDARD.md.
The migrator provides multiple technical strategies to convert nested GitLab paths to flat GitHub repository names. These strategies are used to generate names that should then conform to the naming standard above.
Uses the starting group name (from config group_path) and repository name:
- With
group_path: "sikatech/eshop":sikatech/eshop/services/core-service→eshop-core-servicesikatech/eshop/payment-gateway-adapters/zarinpal-adapter-service→eshop-zarinpal-adapter-service
- The starting group is the last part of the
group_pathin config (e.g., "eshop" from "sikatech/eshop")
Converts all slashes to hyphens:
sikatech/eshop/services/core-service→sikatech-eshop-services-core-servicesikatech/eshop/payment-gateway-adapters/zarinpal-adapter-service→sikatech-eshop-payment-gateway-adapters-zarinpal-adapter-service
Uses first and last segment:
sikatech/eshop/services/core-service→sikatech-core-servicesikatech/eshop/payment-gateway-adapters/zarinpal-adapter-service→sikatech-zarinpal-adapter-service
Uses only the repository name:
sikatech/eshop/services/core-service→core-servicesikatech/eshop/payment-gateway-adapters/zarinpal-adapter-service→zarinpal-adapter-service
Use custom mappings in configuration to ensure names conform to the naming standard:
{
"migration": {
"naming_strategy": "custom",
"name_mappings": {
"sikatech/eshop/services/core-service": "core-service",
"sikatech/eshop/payment-gateway-adapters/zarinpal-adapter-service": "zarinpal-finance-service",
"sikatech/eshop/external/darian-endpoint": "darian-connector-service"
}
}
}Note: When using custom strategy, ensure all mappings follow the GitHub Repository Naming Standard.
The migrator supports two approaches:
- Discovery: Lists all repositories in the specified GitLab group (including nested groups)
- Name Mapping: Converts GitLab paths to GitHub-compatible names using the selected strategy, ensuring names conform to the GitHub Repository Naming Standard
- Validation: Validates repository names against the naming standard and suggests fixes if needed
- Code Migration: Clones repository with full history and pushes to GitHub
- Metadata Migration: Migrates issues, labels, milestones, merge requests, and wikis
- Verification: Checks migration status and reports results
See the Two-Phase Migration Approach section above for detailed steps. This approach allows you to:
- Export all repositories to a JSON file
- Review and edit name mappings before migration
- Validate all mappings against the naming standard
- Migrate from the reviewed JSON file
- Go 1.22.3 or higher
- Git installed and available in PATH
- GitLab API token with appropriate permissions
- GitHub API token with repository creation permissions
Create a personal access token with:
apiscoperead_repositoryscoperead_userscope
Create a personal access token or organization token with:
reposcope (full control of private repositories)admin:orgscope (if migrating to an organization)
- Wiki migration requires manual steps (GitHub wikis are separate git repositories)
- Some GitLab-specific features may not have direct GitHub equivalents
- Large repositories may take significant time to migrate
- Rate limiting may affect migration speed for large numbers of repositories
The service provides detailed error reporting:
- Per-repository error tracking
- Warnings for name changes
- Migration status for each repository
- Detailed logs for troubleshooting
See LICENSE file for details.