| name | add-import-source |
| description | Comprehensive guide for adding new source control systems to the migration framework. Use this skill when asked to add support for importing from a new source system (like GitLab, Perforce, Mercurial, etc.), or when troubleshooting import source issues. Includes workflow modifications, script updates, and credential configuration guidance. |
Add Import Source
Overview
This skill provides step-by-step instructions for adding support for a new source control system to the migration framework. The framework currently supports Azure DevOps (ADO), BitBucket, Subversion (SVN), and GitHub as import sources.
When to Use This Skill
Use this skill when:
- Adding support for a new source control system (GitLab, Perforce, Mercurial, etc.)
- Extending existing source system support with new authentication methods
- Troubleshooting import source detection or cloning issues
- Updating import source URL patterns or parsing logic
Current Supported Sources
The framework currently supports these import sources:
-
Azure DevOps (ADO) - Git repositories
- URL pattern:
https://dev.azure.com/{org}/{project}/_git/{repo}
- Auth: Personal Access Token (PAT)
- Environment variable:
ADO_PAT
-
Azure DevOps TFS - Team Foundation Server repositories
- URL pattern:
https://dev.azure.com/{org}/{project}/_versionControl
- Auth: Personal Access Token (PAT)
- Environment variable:
ADO_PAT
-
BitBucket - Git repositories
- URL pattern:
https://{bitbucket-domain}/scm/{project}/{slug}.git
- Auth: Username + Password/PAT
- Environment variables:
BB_USERNAME, BB_PAT
-
Subversion (SVN) - SVN repositories
- URL pattern:
https://{svn-domain}/{repo}
- Auth: Service account credentials
- Environment variables:
SUBVERSION_SERVICE_USERNAME, SUBVERSION_SERVICE_PASSWORD
-
GitHub (internal) - Same organization
- URL pattern:
https://github.com/htekdev/{repo}
- Auth: GitHub App token
- Environment variable: Handled by
modules.ps1
-
GitHub External - Different organization
- URL pattern:
https://github.com/{org}/{repo}
- Auth: Personal Access Token
- Environment variable:
GH_PAT
Adding a New Import Source
Step 1: Identify Source System Requirements
Before adding a new source, gather these details:
- Source system name (e.g., GitLab, Perforce, Mercurial)
- URL pattern - How repositories are identified
- Authentication method - PAT, OAuth, SSH keys, username/password
- Command-line tools - Required CLI tools or libraries
- Special considerations - Folder structures, branch handling, history preservation
Example for GitLab:
- Name:
gitlab
- URL:
https://gitlab.com/{group}/{project}.git
- Auth: Personal Access Token
- Tool:
git clone (native git)
- Special: Supports groups/subgroups
Step 2: Update URL Parsing Script
Modify scripts/New-ImportRepoDetails.ps1 to detect and parse the new source URL.
Add URL pattern matching logic:
# Add after existing source checks, before the final error
# Check if import url is a valid GitLab url
if($env:IMPORT_URL -match 'https://gitlab.com/(?<group>.+)/(?<project>.+?)(?:\.git)?$'){
$results = @{
"source" = "gitlab"
"group" = $Matches['group']
"project" = $Matches['project']
} | ConvertTo-Json -Compress
Write-Output "results=$results" | Out-File -FilePath $env:GITHUB_OUTPUT -Encoding utf8 -Append
Write-Output "IMPORT_SOURCE=gitlab" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
Write-Output "IMPORT_GITLAB_GROUP=$($Matches['group'])" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
Write-Output "IMPORT_GITLAB_PROJECT=$($Matches['project'])" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
return
}
Important: Place new source checks in logical order (most specific patterns first).
Step 3: Update Migration Script
Modify scripts/New-GitHubRepoMigration.ps1 to handle the new source type.
Add authentication and cloning logic:
# Add in the try block, before the existing if/elseif chain
if($env:IMPORT_SOURCE -eq "gitlab"){
if([String]::IsNullOrEmpty($env:GITLAB_PAT)){
throw "Environment variable GITLAB_PAT is not set"
}
$gitlabGroup = $env:IMPORT_GITLAB_GROUP
$gitlabProject = $env:IMPORT_GITLAB_PROJECT
$cloneUrl = "https://oauth2:$($env:GITLAB_PAT)@gitlab.com/$gitlabGroup/$gitlabProject.git"
}
Add any special handling after the main cloning logic:
# After: git clone $cloneUrl ado
# Add special processing if needed
if($env:IMPORT_SOURCE -eq "gitlab"){
# GitLab-specific post-clone operations
Push-Location $folder/ado
# Handle GitLab-specific branch naming, tags, etc.
Pop-Location
}
Step 4: Update Error Messages
Update error messages to include the new source:
In New-GitHubRepoMigration.ps1:
else{
Write-Output "| **Import Repo** | ❌ | Unknown import source: ``$($env:IMPORT_SOURCE)`` <b>Expected values:</b> ``ado``, ``bitbucket``, ``gitlab``, ``svn`` |" | Out-File -FilePath $env:JOB_SUMMARY_FILE -Encoding utf8 -Append
throw "Unknown import source: $env:IMPORT_SOURCE, expected values: ado, bitbucket, gitlab, svn, etc."
}
In New-ImportRepoDetails.ps1 (final error):
throw "Unknown import url: $env:IMPORT_URL`nExpected format: `n 1. https://dev.azure.com/{organization}/{project}/_git/{repo}`n 2. https://bitbucket.example.com/scm/{project}/{slug}.git`n 3. https://gitlab.com/{group}/{project}.git`n 4. https://svn.example.com/{repo}`n 5. None"
Step 5: Configure Required Secrets
CRITICAL: Users must add the necessary credentials to their repository secrets.
GitHub Organization Secrets (Recommended)
For organization-wide use, add secrets at the organization level:
- Navigate to Organization Settings → Secrets and Variables → Actions
- Click "New organization secret"
- Add the credential (e.g.,
GITLAB_PAT)
- Set repository access (all repositories or specific ones)
Repository Secrets (Alternative)
For repository-specific credentials:
- Navigate to Repository Settings → Secrets and Variables → Actions
- Click "New repository secret"
- Add the credential
Secret Naming Conventions
Follow the existing pattern:
{SOURCE}_PAT - Personal Access Token
{SOURCE}_USERNAME - Username (if needed)
{SOURCE}_PASSWORD - Password (if separate from PAT)
{SOURCE}_SERVICE_USERNAME - Service account username
{SOURCE}_SERVICE_PASSWORD - Service account password
Example for GitLab:
GITLAB_PAT - GitLab Personal Access Token
GITLAB_USERNAME - (Optional) If using username/password auth
Step 6: Update Workflow to Pass Secrets
Modify .github/workflows/migrate.yml to pass the new secret to the migration job.
In the test job, add environment variables:
- name: 🏃 Migrate ADO Repo to GitHub Repo
id: import
if: ${{ fromJson(steps.url.outputs.results).source != 'none' }}
env:
GH_APP_CERTIFICATE: ${{ secrets.GH_APP_PRIVATE_KEY }}
GH_APP_ID: ${{ vars.GH_APP_ID }}
ADO_PAT: ${{ secrets.ADO_PAT }}
BB_USERNAME: ${{ secrets.BB_USERNAME }}
BB_PASSWORD: ${{ secrets.BB_PAT }}
GITLAB_PAT: ${{ secrets.GITLAB_PAT }}
ORG: ${{ env.GH_ORG }}
REPO: ${{ env.GH_REPO }}
Step 7: Install Required Tools (if needed)
If the new source requires special command-line tools, add installation steps in the workflow.
Add before the migration step:
- name: 📦 Install GitLab Tools
if: ${{ fromJson(steps.url.outputs.results).source == 'gitlab' }}
run: |
# Install any required tools
# Example: pip install gitlab-cli
# Or: apt-get install gitlab-tools
For tools that need compilation or complex setup, create a dedicated script in scripts/ directory:
# scripts/Install-GitLabTools.ps1
# Tool installation logic
Step 8: Handle Source-Specific Features
Some sources may have unique features requiring special handling:
Large File Storage (LFS)
if($env:IMPORT_SOURCE -eq "gitlab"){
Push-Location $folder/ado
# Check for LFS
if(Test-Path ".gitattributes"){
git lfs fetch --all
git lfs checkout
}
Pop-Location
}
Branch Protection
if($env:IMPORT_SOURCE -eq "gitlab"){
# GitLab protected branches need special handling
Push-Location $folder/ado
# Identify and migrate branch protection rules
Pop-Location
}
Webhooks and Integrations
# Document webhooks for manual migration
Write-Output "| **GitLab Webhooks** | ⚠️ | Manual migration required. See documentation. |" | Out-File -FilePath $env:JOB_SUMMARY_FILE -Encoding utf8 -Append
Step 9: Add Post-Migration Finalization (Optional)
If the source has ADO-style finalization requirements (pipeline rewiring, integration setup), create a finalization script:
# scripts/Execute-GitLabMigrationFinalization.ps1
# Similar to Execute-ADOMigrationFinalization.ps1
Add to workflow:
- name: 🏃 GitLab Integration Setup
continue-on-error: true
if: ${{ fromJson(steps.url.outputs.results).source == 'gitlab' }}
env:
GITLAB_PAT: ${{ secrets.GITLAB_PAT }}
run: |
& "${{github.workspace}}/scripts/Execute-GitLabMigrationFinalization.ps1" -githubRepoUrl $($env:GITHUB_REPO_URL)
Step 10: Document Credential Setup for Users
Create clear documentation for users on how to set up credentials. Add to your repository's documentation:
For GitLab Import Source
Prerequisites:
- GitLab Personal Access Token with
read_repository scope
Setup Steps:
-
Generate GitLab Personal Access Token:
- Go to GitLab Settings → Access Tokens
- Create token with scopes:
read_repository, read_api
- Copy the token (shown only once)
-
Add Token to GitHub Secrets:
- Go to GitHub Organization/Repository Settings
- Navigate to Secrets and Variables → Actions
- Click "New organization secret" (or "New repository secret")
- Name:
GITLAB_PAT
- Value: Paste your GitLab token
- Click "Add secret"
-
Verify Configuration:
- Run a test migration with a GitLab repository URL
- Check workflow logs for authentication success
- Verify repository content was cloned correctly
Troubleshooting:
- 401 Unauthorized: Token expired or invalid
- 403 Forbidden: Token lacks required scopes
- 404 Not Found: Repository path incorrect or token lacks access
Testing the New Source
Unit Testing
- Create a test repository in the new source system
- Generate test credentials with minimal permissions
- Run migration workflow manually with test parameters
- Verify all stages complete successfully
Integration Testing
- Test with various repository sizes (small, medium, large)
- Test with different branch structures
- Test with edge cases (empty repos, archived repos)
- Verify history preservation
- Check file integrity and commit metadata
Error Handling Testing
- Test with invalid credentials
- Test with malformed URLs
- Test with inaccessible repositories
- Verify error messages are clear and actionable
Advanced: Custom Authentication Patterns
Some source systems use custom auth:
OAuth 2.0 Flow
if($env:IMPORT_SOURCE -eq "custom-source"){
# Get OAuth token
$response = Invoke-RestMethod -Method Post -Uri "https://custom-source.com/oauth/token" `
-Body @{
client_id = $env:CUSTOM_SOURCE_CLIENT_ID
client_secret = $env:CUSTOM_SOURCE_CLIENT_SECRET
grant_type = "client_credentials"
}
$token = $response.access_token
$cloneUrl = "https://oauth2:$($token)@custom-source.com/$repo.git"
}
SSH Key Authentication
if($env:IMPORT_SOURCE -eq "ssh-source"){
# Configure SSH key
$sshKey = $env:SSH_PRIVATE_KEY
$sshKeyPath = "$HOME/.ssh/id_rsa"
New-Item -ItemType Directory -Force -Path "$HOME/.ssh"
Set-Content -Path $sshKeyPath -Value $sshKey
chmod 600 $sshKeyPath
# Add to known hosts
ssh-keyscan ssh-source.com >> "$HOME/.ssh/known_hosts"
$cloneUrl = "git@ssh-source.com:$org/$repo.git"
}
API Token with Custom Header
if($env:IMPORT_SOURCE -eq "api-source"){
# Configure git credential helper
git config --global credential.helper store
# Create credentials file
$credentials = "https://api-token:$($env:API_SOURCE_TOKEN)@api-source.com"
Set-Content -Path "$HOME/.git-credentials" -Value $credentials
$cloneUrl = "https://api-source.com/$org/$repo.git"
}
Common Patterns and Best Practices
URL Parsing Best Practices
- Use specific regex patterns - Avoid overly broad matches
- Test regex thoroughly - Include edge cases in tests
- Handle optional components - Like
.git suffix, auth prefixes
- Escape special characters - In regex patterns
- Document expected formats - In error messages
Credential Management
- Never log credentials - Use Write-Host for debugging, not Write-Output
- Use secret masking - GitHub Actions automatically masks registered secrets
- Minimum required permissions - Request only necessary scopes
- Rotate credentials regularly - Document rotation procedures
- Use service accounts - Prefer service accounts over personal tokens
Error Handling
- Validate early - Check for credentials before starting migration
- Provide context - Include source URL in error messages
- Suggest remediation - Tell users how to fix the problem
- Log to job summary - Use the job summary file for user-visible errors
- Fail gracefully - Clean up partial migrations on error
Performance Optimization
- Shallow clones - Use
--depth 1 for repositories without history requirements
- Sparse checkout - For folder-specific migrations
- Parallel processing - For bulk migrations (future enhancement)
- Compression - Enable git compression for large repositories
- Network optimization - Use mirrors or proxies when available
Example: Complete GitLab Implementation
Here's a complete example of adding GitLab support:
1. Update New-ImportRepoDetails.ps1
# Add after BitBucket check
if($env:IMPORT_URL -match 'https://gitlab.com/(?<group>.+)/(?<project>.+?)(?:\.git)?$'){
$results = @{
"source" = "gitlab"
"group" = $Matches['group']
"project" = $Matches['project']
} | ConvertTo-Json -Compress
Write-Output "results=$results" | Out-File -FilePath $env:GITHUB_OUTPUT -Encoding utf8 -Append
Write-Output "IMPORT_SOURCE=gitlab" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
Write-Output "IMPORT_GITLAB_GROUP=$($Matches['group'])" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
Write-Output "IMPORT_GITLAB_PROJECT=$($Matches['project'])" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
return
}
2. Update New-GitHubRepoMigration.ps1
# Add after BitBucket handling
elseif($env:IMPORT_SOURCE -eq "gitlab"){
if([String]::IsNullOrEmpty($env:GITLAB_PAT)){
throw "Environment variable GITLAB_PAT is not set"
}
$gitlabGroup = $env:IMPORT_GITLAB_GROUP
$gitlabProject = $env:IMPORT_GITLAB_PROJECT
$cloneUrl = "https://oauth2:$($env:GITLAB_PAT)@gitlab.com/$gitlabGroup/$gitlabProject.git"
}
3. Update Workflow
env:
GITLAB_PAT: ${{ secrets.GITLAB_PAT }}
4. User Documentation
README.md Addition:
### Supported Import Sources
- Azure DevOps (ADO)
- BitBucket
- GitLab
- Subversion (SVN)
- GitHub
#### GitLab Setup
1. Generate a Personal Access Token in GitLab with `read_repository` scope
2. Add `GITLAB_PAT` secret to your GitHub organization or repository
3. Use GitLab repository URL format: `https://gitlab.com/{group}/{project}`
Troubleshooting Guide
Source Not Detected
Symptom: "Unknown import url" error
Solutions:
- Verify URL format matches the regex pattern exactly
- Check for typos in the domain or path
- Ensure the regex is placed in the correct order in
New-ImportRepoDetails.ps1
- Test regex pattern using PowerShell:
"url" -match 'pattern'
Authentication Failures
Symptom: 401 or 403 errors during clone
Solutions:
- Verify secret name matches exactly (case-sensitive)
- Check token hasn't expired
- Confirm token has required permissions/scopes
- Test token manually:
git clone https://token@source.com/repo.git
Clone Hangs or Times Out
Symptom: Workflow hangs during git clone
Solutions:
- Check repository size (consider shallow clone for large repos)
- Verify network connectivity to source
- Check for rate limiting on source system
- Increase timeout values in workflow
Missing Branches or History
Symptom: Not all branches migrated
Solutions:
- Use
--mirror flag for full clone: git clone --mirror
- Check default branch handling logic
- Verify branch patterns match source system
- Test with:
git branch -a after clone
Special Characters in URLs
Symptom: URL parsing fails with special characters
Solutions:
- URL-encode special characters before parsing
- Use
[System.Web.HttpUtility]::UrlDecode() for decoding
- Handle authentication credentials separately from URL
- Test with various special character combinations
Security Considerations
- Credential Storage: Never commit credentials or expose them in logs
- Least Privilege: Request minimum required permissions for migration
- Token Expiration: Document token rotation procedures
- Audit Trail: Log migration activities for compliance
- Data Exposure: Be cautious when migrating between different security contexts
- Network Security: Use HTTPS for all connections, validate certificates
- Secret Sprawl: Consolidate credentials when possible
Related Files
scripts/New-ImportRepoDetails.ps1 - URL parsing and source detection
scripts/New-GitHubRepoMigration.ps1 - Main migration logic and cloning
scripts/Execute-GitImport.ps1 - Git push operations
.github/workflows/migrate.yml - Workflow orchestration
scripts/modules.ps1 - Common utility functions
Future Enhancements
Consider these enhancements when adding new sources:
- Parallel migrations - Support bulk migrations
- Partial history - Allow date-range or commit-range imports
- Incremental sync - Keep source and GitHub in sync
- Metadata preservation - Migrate issue trackers, wikis, etc.
- Dry-run mode - Validate migration before execution
- Rollback capability - Undo failed migrations
- Source validation - Pre-check source accessibility before migration