Custom Transformations
Custom Transformations
Section titled “Custom Transformations”For complex scenarios, Copybara supports custom transformations with reversal logic.
core.transform
Section titled “core.transform”Wrap transformations with explicit reversal:
core.transform( transformations = [ core.move("src/", "lib/"), core.replace("old", "new"), ], reversal = [ core.replace("new", "old"), core.move("lib/", "src/"), ],)Why Reversal?
Section titled “Why Reversal?”Reversal is needed for bidirectional workflows:
Export: internal → external (apply transformations)Import: external → internal (apply reversal)Example: URL Transform
Section titled “Example: URL Transform”core.transform( transformations = [ core.replace("internal.corp.com", "api.example.com"), ], reversal = [ core.replace("api.example.com", "internal.corp.com"), ],)Checking Reversibility
Section titled “Checking Reversibility”Enable reversibility checking:
core.workflow( reversible_check = True, # Fail if transforms aren't reversible ...)Dynamic Transformations
Section titled “Dynamic Transformations”Use Starlark functions for dynamic behavior:
def _dynamic_transform(ctx): for f in ctx.run(glob(["**/*.md"])): content = ctx.read_path(f) if "DEPRECATED" in content: ctx.write_path(f, content.replace("DEPRECATED", "LEGACY")) return ctx.success()
core.dynamic_transform( impl = _dynamic_transform,)Context Methods
Section titled “Context Methods”| Method | Description |
|---|---|
ctx.run(glob) | Get matching files |
ctx.read_path(path) | Read file contents |
ctx.write_path(path, content) | Write file contents |
ctx.success() | Return success |
ctx.noop(msg) | Return no-op |
ctx.error(msg) | Return error |
Conditional Transformations
Section titled “Conditional Transformations”Apply transformations conditionally:
def _conditional_transform(ctx): # Only transform if certain file exists if ctx.run(glob(["config.json"])): ctx.run(core.replace("dev", "prod")) return ctx.success()Non-Reversible Transforms
Section titled “Non-Reversible Transforms”Some operations can’t be reversed:
# These are NOT reversible:core.remove(glob(["**/*.tmp"])) # Files are gonecore.replace("secret", "***") # Original value lost
# Mark as not reversiblecore.workflow( reversible_check = False, # Don't check reversibility ...)Transform Sequences
Section titled “Transform Sequences”Use core.transform to group related operations:
# Group related URL transformationsurl_transforms = core.transform( transformations = [ core.replace("internal.corp/repo", "github.com/org/repo"), core.replace("internal.corp/docs", "docs.example.com"), ], reversal = [ core.replace("github.com/org/repo", "internal.corp/repo"), core.replace("docs.example.com", "internal.corp/docs"), ],)
# Group related path transformationspath_transforms = core.transform( transformations = [ core.move("internal/src/", "src/"), ], reversal = [ core.move("src/", "internal/src/"), ],)
# Use in workflowtransformations = [ url_transforms, path_transforms,]Variables in Starlark
Section titled “Variables in Starlark”Use variables for reusability:
# Define onceINTERNAL_HOST = "internal.corp.com"PUBLIC_HOST = "api.example.com"
# Use in transformstransformations = [ core.replace(INTERNAL_HOST, PUBLIC_HOST),]Complete Bidirectional Example
Section titled “Complete Bidirectional Example”# Reusable transform definitionsexport_transforms = core.transform( transformations = [ core.move("internal/src/", "src/"), core.remove(glob(["**/internal/**"])), core.replace("internal.corp.com", "api.example.com"), ], reversal = [ core.replace("api.example.com", "internal.corp.com"), core.move("src/", "internal/src/"), ],)
# Export workflowcore.workflow( name = "export", origin = git.origin(url = internal_url, ref = "main"), destination = git.github_destination(url = github_url, push = "main"), transformations = [export_transforms], reversible_check = True,)
# Import workflow (uses reversal automatically)core.workflow( name = "import", origin = git.github_pr_origin(url = github_url, branch = "main"), destination = git.gerrit_destination(url = internal_url, ...), transformations = [export_transforms], # Copybara uses reversal mode = "CHANGE_REQUEST_FROM_SOT",)