GitLab to GitHub Migration Guide
Step-by-step checklist for migrating repositories from GitLab to GitHub. Each repo follows the same per-repo process; cross-cutting tasks are handled once after all repos are moved.
For full repo details, see the Repository Inventory. For CI translation patterns, see the CI/CD Translation Reference.
Naming Convention​
GitHub repos use flat namespace. For nested GitLab subgroup repos:
| GitLab Pattern | GitHub Pattern |
|---|---|
publicala/<name> | <name> (as-is) |
publicala/fenice/dev/<name> | fenice-<name> |
publicala/fenice/dep/env/<name> | fenice-<name> |
publicala/fenice/dep/dependencies/<name> | fenice-<name> |
publicala/fenice-legacy-archived/<name> | <name> (already prefixed) |
publicala_exercises/<name> | <name> |
Phase 1: Setup (one-time)​
Tools​
- Install GitHub CLI:
brew install gh - Install Actions Importer:
gh extension install github/gh-actions-importer - Verify:
gh actions-importer version(expect v1.3.6+) - Authenticate:
gh auth loginwith org admin access - Configure Actions Importer:
gh actions-importer configure(set GitLab instance URL, tokens)
Organization Settings​
- Review
publicalaorg settings on GitHub - Configure default repository permissions (read for members)
- Create org-level ruleset for
mainbranches (ruleset #14411941):- Require pull request reviews (1 approval)
- No force pushes
- No branch deletion
- Status checks: configured per-repo (job names vary)
- Create org-level ruleset for
devbranches (ruleset #15112321):- No force pushes
- No branch deletion (prevents
devbeing auto-deleted bydelete_branch_on_mergeafter adevtomainPR merges) - No PR or status-check requirements (direct pushes to
devremain allowed)
- Set up org-level secrets (migrated from GitLab group-level CI/CD variables):
CLOUDFLARE_ACCOUNT_IDSINGLESTORE_LICENSESLACKWEBHOOK_ENG_DEPLOYSSLACKWEBHOOK_ENG_PRODUCTION_DEPLOYS
Team Members​
Invite all team members to the publicala GitHub Organization (not the Enterprise account directly). Organization membership grants repo access via teams and roles; Enterprise seats are consumed automatically when a member joins an org under it.
- Invite the following members via GitHub API (completed 2026-03-18). Enterprise Cloud licenses were manually increased from 7 to 20 before sending invitations:
| Name | |
|---|---|
| MartÃn Alejandro Paz | apaz@publica.la |
| Juan Pablo Locatelli | jplocatelli@publica.la |
| Valentina Rojas | vrojas@publica.la |
| Gonzalo Parra | gparra@publica.la |
| Augusto Rucle | arucle@publica.la |
| Kijam López | klopez@publica.la |
| Nicolás Parola | nparola@publica.la |
| Cecilia Saleme | csaleme@publica.la |
| Nicolás Cabal Cullen | ncabal@publica.la |
| Anabella Pippo | apippo@publica.la |
| AgustÃn Acevedo | aacevedo@publica.la |
| José Luis Tanicuchà | jtanicuchi@publica.la |
| Lara Macarena Sastre | lsastre@publica.la |
| Lucca Costa Leandro | lcosta@publica.la |
| Francisco Laurino | flaurino@publica.la |
- Assign org role:
Memberfor all (Owners: Franco, Nicolas F already set) - Created
Core TeamGitHub Team with Write access (2026-03-24). All org members added. See Access Management for usage. - Verify all members have accepted invitations
- Clean up duplicate account: Ignacio Milano has both
igmilanoandimilano-pla
Tracking Invitation Acceptance​
# List pending invitations (decreases as members accept)
gh api /orgs/publicala/invitations --jq '.[] | {email: .email, role: .role}'
# Count remaining pending
gh api /orgs/publicala/invitations --jq 'length'
# List current org members
gh api /orgs/publicala/members --jq '.[].login'
Third-Party Runners (Depot)​
- Sign up for Depot Startup plan
- Connect GitHub org to Depot
- Configure runner labels in workflows per the runners reference (default label, sizing policy, PHP setup tiers). Background: CI Runner Strategy.
- Test with a pilot repo before Tier 4
Phase 2: Per-Repo Migration Checklist​
Repeat for each repository. Copy this checklist as a template.
2.1 Mirror the Repository​
Commands: clone, create, push
# Clone all refs from GitLab
git clone --mirror git@gitlab.com:publicala/<repo>.git
cd <repo>.git
# For LFS repos (castoro, volpe): fetch LFS objects
git lfs fetch --all
# Create the GitHub repo
gh repo create publicala/<github-name> --private --confirm
# Push everything to GitHub
git push --mirror git@github.com:publicala/<github-name>.git
# For LFS repos: push LFS objects
git lfs push --all git@github.com:publicala/<github-name>.git
2.2 Rename Default Branch to main​
Skip if already on main. For repos on master, production, or other branches.
Commands: rename branch
# On GitHub (after mirror push)
gh api repos/publicala/<github-name> -X PATCH -f default_branch=main
# Or via git
git clone git@github.com:publicala/<github-name>.git
cd <github-name>
git branch -m master main
git push -u origin main
git push origin --delete master
Update the default branch in GitHub repo settings.
2.3 Verify the Mirror​
- Compare branch count: GitLab vs GitHub
- Compare tag count: GitLab vs GitHub
- Verify latest commit SHA matches on default branch
- For LFS repos: verify LFS objects are accessible (
git lfs ls-files)
2.4 Migrate CI/CD (repos with .gitlab-ci.yml)​
Commands: actions-importer dry-run
# Dry-run: preview the translation
gh actions-importer dry-run gitlab \
--output-dir output/<repo> \
--namespace publicala \
--project <repo>
# Review the generated workflow files in output/<repo>/.github/workflows/
# Manually adjust as needed (see CI/CD Translation Reference)
Common adjustments after dry-run:
- Replace
services: [docker:dind]with appropriate Docker setup - Replace GitLab-specific variables (
CI_JOB_TOKEN,CI_REGISTRY, etc.) - Convert
resource_group:toconcurrency:groups - Replace
when: manualwithworkflow_dispatchor environment protection rules - Update cache configuration (GitLab
cache:toactions/cache) - Replace GitLab Pages deployment with Cloudflare Pages or GitHub Pages action
- Update Sentry release/deploy notification commands
Commit the workflow files to .github/workflows/ in the repo.
2.5 Configure Environments and Secrets​
GitHub has three secret scopes: organization, repository, and environment. Deploy-related secrets (deploy hooks, API tokens) should be scoped to their environment so they're only exposed to jobs that declare that environment.
Use the three-environment pattern (staging, staging-review, production) for repos with deploy gates. See Deploy Approval Pattern for the full setup (environments, reviewers, secrets, workflow examples). Summary checklist:
- Create GitHub Environments matching workflow
environment:declarations. For deploy-gated repos:staging(no reviewer),staging-review(Core Team reviewer, PR previews),production(Core Team reviewer) - Add deploy secrets to the corresponding environment.
LARAVEL_CLOUD_STAGING_DEPLOY_HOOKgoes in bothstagingandstaging-review(same URL);LARAVEL_CLOUD_PRODUCTION_DEPLOY_HOOKinproduction;VAPOR_API_TOKENin all three for Vapor repos - Use org-level secrets for shared values:
SINGLESTORE_LICENSE,CLOUDFLARE_ACCOUNT_ID,SLACKWEBHOOK_ENG_DEPLOYS,SLACKWEBHOOK_ENG_PRODUCTION_DEPLOYS - Use repository secrets for repo-specific values that aren't tied to a deploy environment
- Verify secret names match workflow references
2.6 Set Branch Protection​
- Apply branch protection rules to
main - Configure required status checks. Migrated Laravel repos converged on these job names:
build,lint,phpstan,test. Repos with a JS build addbuild-jsand (optionally)lint-js; Vapor repos optionally addphpcpd. Use these names verbatim in new workflows so status-check configuration is copy-pasteable across repos - Verify auto-delete head branches is enabled (automatically deletes the PR source branch after merge). Applied org-wide on 2026-04-24 to all 28 active repos; GitHub offers no org-level default, so confirm the toggle is still on for the repo being migrated and for any newly created repo
2.7 Update Deploy Integrations​
Laravel Cloud deploy hooks are async. The curl returns immediately; deployment happens in the background. Remove any post-deploy health check scripts that expect the deploy to be done.
Per-target deploy integration steps
Laravel Cloud:
- Update Laravel Cloud environment to connect to GitHub repo
- Regenerate deploy hook URLs if needed
- Update
LARAVEL_CLOUD_*_DEPLOY_HOOKsecrets
Laravel Vapor:
- Continue using deploy hooks from GitHub Actions (Vapor does not support GitHub as a source)
Cloudflare Workers/Pages:
- Update Cloudflare Pages project to connect to GitHub repo (if using Cloudflare Git integration)
- Or keep Wrangler CLI deploy from GitHub Actions
npm Registry (Delfino):
- Configure GitHub Packages authentication in workflow
- Update
publishConfiginpackage.jsonto point to GitHub Packages - Update consumer repos to pull from new registry
2.8 Verify End-to-End​
- Push a test commit to a feature branch
- Verify CI runs successfully
- Create and merge a PR
- Verify deployment triggers correctly (if applicable)
2.9 Update Local Development​
- Update remote URL:
git remote set-url origin git@github.com:publicala/<github-name>.git - If the default branch was renamed (e.g.
master→main), rename the local branch and reset upstream:git branch -m master main
git fetch origin
git branch -u origin/main main
git remote set-head origin -a - Verify
git pullandgit pushwork
2.10 Archive on GitLab​
- Mark the GitLab repo as archived (read-only)
- Add a note to the GitLab repo description:
Migrated to GitHub: github.com/publicala/<github-name>
Phase 3: Cross-Cutting Updates​
After all repos (or a significant batch) are migrated:
Laravel Cloud
- Reconnect all Laravel Cloud environments to GitHub repos
- Verify deploy hooks trigger from GitHub Actions
- Test staging and production deploys for each Cloud service
Sentry
- Update Sentry project integrations to watch GitHub repos
- Update deploy notification webhooks in GitHub Actions
- Verify source map uploads reference correct repo
Local Development (zoo)
- Update
zoo/docker-compose files to reference GitHub remotes - Update any git clone/pull scripts in zoo
- Update developer onboarding documentation
- Notify team to update local clones (see step 2.9)
npm Registry (Delfino)
- Publish
@publicala/delfinoto GitHub Packages - Update all consumer repos (
farfalla,volpe, etc.) to pull from GitHub Packages - Verify
npm install/yarn installresolves correctly - Remove GitLab Packages publishing from old CI
Documentation Hosting
- Reconnect Cloudflare Pages
docsproject to use GitHub as source (currently connected to GitLab) - Set up Cloudflare Pages project for
cricetotest reports - Remove GitLab Pages configuration
Notifications
- Evaluate if
gitlab-to-slack-proxyis still needed - Set up GitHub native Slack integration for the
publicalaorg - Configure notification channels per repo/event type
Cleanup
- Verify all 34 active repos are on GitHub and functional
- Verify all GitLab repos are archived
- Update CLAUDE.md files across all projects
- Update this migration documentation with final status
- Remove GitLab-specific tooling and scripts (glab, GitLab tokens in CI)