Use this page when a team is moving source control or CI to a new platform. The same method works in any direction. The table below is one common mapping. Swap the syntax for your systems.
Migrate in waves, never all at once
flowchart LR I[Inventory repos] --> R[Rank by risk and value] R --> P[Pilot 1 low-risk repo] P --> T[Turn the pilot into a template] T --> W[Wave of 5-10 repos] W --> C[Cut over and archive old pipeline] C --> W
- Inventory: every repo, its CI file, what it deploys, to which account and environment, and whether it is still used.
- Rank: archive dead repos first (cheapest migration is none), then pilot with a low-traffic service, then high-value services, then the hard ones.
- Template: a shared CI template (
include:) that every migrated repo uses, so fixes land once. - Waves: small batches with a cutover date each.
Map the old pipeline to the new one
| Example source A | Example source B | Example destination |
|---|---|---|
pipelines.default |
multibranch default | jobs with rules: - if: $CI_PIPELINE_SOURCE == "push" |
pipelines.branches.main |
when { branch 'main' } |
rules: - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH |
pipelines.pull-requests |
PR builder | rules: - if: $CI_PIPELINE_SOURCE == "merge_request_event" |
pipelines.custom (manual) |
parameterized job | when: manual |
step |
stage / sh |
job |
parallel |
parallel {} |
jobs in the same stage |
caches |
workspace | cache: |
artifacts |
archiveArtifacts |
artifacts: |
deployment: production |
input step | environment: production plus protected environment |
| repository variables | credentials store | CI/CD variables (masked, protected) or OIDC |
pipe: atlassian/aws-* |
AWS plugin | OIDC role plus aws CLI in an image |
Port one repo
- Clean clone, on a new branch
ci/migration. - Translate every step. Leave a
# TODO(port):comment where behaviour is unclear rather than guessing. - Replace stored cloud keys with OIDC.
- Production stays manual and protected; see blue-green deploys.
- Validate the file before opening the MR.
# Lint the new CI file against your CI provider (read-only call)
ci-lint ci.yml
# Open as a draft MR; never push to the default branch
git switch -c ci/migration
git add ci.yml && git commit -m "ci: port pipeline to the new system"
git push -u origin ci/migration
# Open a draft merge request with your git host's CLI
Prove parity before cutover
- Same build artifact (compare image digest inputs, test counts, artifact names).
- Same tests run and the same number pass.
- A deploy to dev from the new pipeline works and the service is healthy.
- Required CI/CD variables exist, are masked and protected where needed.
- Run both pipelines side by side for at least one real change.
Cut over
- Freeze merges to the old repo for the cutover window.
- Final mirror sync of branches and tags.
- Make the new repo the source of truth; set the old one read-only or archived.
- Disable the old pipeline (do not delete it yet; keep it for 30 days).
- Update webhooks, deploy keys and anything else that points at the old URL.
- Announce, with the new URL and who to ask.
Rollback
Until the old pipeline is deleted, rollback is: re-enable the old pipeline, un-archive the old repo, and push any commits made in the new repo back with a mirror sync. Keep that window at least one release cycle.
Pitfalls
- Copying secrets into new CI variables by hand without rotating them. Treat migration as a rotation opportunity; see secret rotation.
- Plaintext secrets inside the old CI file or build specs. Find them first and rotate them.
- Self-hosted runners with broad cloud rights. Scope runners per environment.
- Repos nobody owns. Assign an owner or archive them before migrating.