Transformations Overview
Transformations
Section titled “Transformations”Transformations modify code as it moves from origin to destination. They’re the core of what makes Copybara powerful.
How Transformations Work
Section titled “How Transformations Work”Origin Files → [Transform 1] → [Transform 2] → [Transform N] → Destination FilesTransformations are applied in order, each receiving the output of the previous one.
transformations = [ core.move("src/", "lib/"), # First: restructure core.replace("old", "new"), # Then: replace text core.verify_match("SECRET", ...), # Finally: verify]Transformation Types
Section titled “Transformation Types”| Category | Transformations |
|---|---|
| File operations | core.move, core.copy, core.remove |
| Text replacement | core.replace, core.filter_replace |
| Verification | core.verify_match |
| Metadata | metadata.squash_notes, metadata.add_header, etc. |
| Custom | core.transform with reversal |
Quick Reference
Section titled “Quick Reference”Moving Files
Section titled “Moving Files”# Move directorycore.move("src/", "lib/")
# Move to rootcore.move("project/src/", "")
# Move single filecore.move("old-name.txt", "new-name.txt")Replacing Text
Section titled “Replacing Text”# Simple replacementcore.replace("old_text", "new_text")
# With regexcore.replace( before = "version = ${ver}", after = "version = ${ver}-public", regex_groups = {"ver": "[0-9]+\\.[0-9]+"},)Filtering Files
Section titled “Filtering Files”# Remove filescore.remove(glob(["**/internal/**"]))
# Remove matching contentcore.replace( before = "// INTERNAL: ${content}\n", after = "", regex_groups = {"content": ".*"},)Verification
Section titled “Verification”# Fail if pattern existscore.verify_match( regex = "API_KEY|SECRET", verify_no_match = True,)Metadata
Section titled “Metadata”# Customize commit messagemetadata.squash_notes( prefix = "Sync from internal:\n", show_author = True,)
# Add header to commitmetadata.add_header("Synced-From: internal")Order Matters
Section titled “Order Matters”Transformations are applied sequentially:
# CORRECT: move first, then replace in new locationtransformations = [ core.move("src/", "lib/"), core.replace("src/", "lib/", paths = glob(["**/*.py"])),]
# INCORRECT: replace references to old path, then movetransformations = [ core.replace("src/", "lib/"), # Still in src/ core.move("src/", "lib/"), # Now paths don't match]Path Filtering
Section titled “Path Filtering”Most transformations support paths parameter:
core.replace( before = "internal.corp", after = "example.com", paths = glob(["**/*.md", "**/*.txt"]), # Only in these files)Reversibility
Section titled “Reversibility”For bidirectional workflows, transformations should be reversible:
core.transform( transformations = [ core.move("src/", "lib/"), ], reversal = [ core.move("lib/", "src/"), ],)Debugging Transformations
Section titled “Debugging Transformations”Use folder destination to see results:
java -jar copybara.jar migrate copy.bara.sky workflow \ --folder-destination /tmp/output
# Inspect the outputls -la /tmp/outputCommon Patterns
Section titled “Common Patterns”Open Sourcing
Section titled “Open Sourcing”transformations = [ # Remove internal directories core.remove(glob(["**/internal/**"])),
# Replace internal URLs core.replace("internal.corp.com", "api.example.com"),
# Remove internal comments core.replace( before = "// INTERNAL:${content}\n", after = "", regex_groups = {"content": ".*"}, ),
# Verify no secrets core.verify_match( regex = "INTERNAL_SECRET|API_KEY_\\w+", verify_no_match = True, ),]Monorepo Extraction
Section titled “Monorepo Extraction”transformations = [ # Move subdirectory to root core.move("packages/my-lib/", ""),
# Update import paths core.replace( before = "@monorepo/my-lib", after = "my-lib", ),]