Importing Contributions
Importing External Contributions
Section titled “Importing External Contributions”The reverse of open sourcing: importing pull requests from external contributors back to your internal repository.
The Challenge
Section titled “The Challenge”External contributions need to:
- Be reviewed internally before integration
- Have paths adjusted for internal structure
- Have internal references restored
- Preserve original authorship
Basic Import Workflow
Section titled “Basic Import Workflow”core.workflow( name = "import",
# Read from external PRs origin = git.github_pr_origin( url = "https://github.com/company/public-repo", branch = "main", required_labels = ["ready-to-import"], # Only labeled PRs ),
# Create internal change for review destination = git.gerrit_destination( url = "https://gerrit.internal.company.com/internal-repo", fetch = "main", push_to_refs_for = "main", ),
# Preserve external contributor info authoring = authoring.pass_thru( default = "External Contributor <external@company.com>", ),
transformations = [ # Reverse the export structure core.move("docs/", "docs/public/"), core.move("src/", "src/public/"),
# Restore internal URLs (reverse of export) core.replace("api.company.io", "internal.company.com"),
# Preserve contribution metadata metadata.expose_label("Co-authored-by"), metadata.expose_label("Signed-off-by"),
# Add import tracking metadata.add_header("Imported-From: github.com/company/public-repo"), ],
mode = "CHANGE_REQUEST_FROM_SOT",)Filtering PRs
Section titled “Filtering PRs”Control which PRs are eligible for import:
origin = git.github_pr_origin( url = "https://github.com/company/public-repo", branch = "main",
# Only PRs with this label required_labels = ["ready-to-import"],
# Only merged PRs state = "MERGED",
# Only if CI passed required_status_context_names = ["ci/build", "ci/test"],)To Gerrit (Internal Code Review)
Section titled “To Gerrit (Internal Code Review)”destination = git.gerrit_destination( url = "https://gerrit.internal/repo", fetch = "main", push_to_refs_for = "main", topic = "external-contribution",)To GitHub (Internal Repo)
Section titled “To GitHub (Internal Repo)”destination = git.github_pr_destination( url = "https://github.internal/repo", destination_ref = "main", pr_branch = "import/${CONTEXT_REFERENCE}", title = "Import: ${COPYBARA_WORKFLOW_NAME}", labels = ["external-contribution"],)Bidirectional Setup
Section titled “Bidirectional Setup”Combine export and import in one config:
INTERNAL = "https://github.com/company/internal"EXTERNAL = "https://github.com/company/public"
# Reusable transforms with reversalurl_transforms = core.transform( transformations = [ core.replace("internal.company.com", "api.company.io"), ], reversal = [ core.replace("api.company.io", "internal.company.com"), ],)
# Export: internal → externalcore.workflow( name = "export", origin = git.github_origin(url = INTERNAL, ref = "main"), destination = git.github_destination(url = EXTERNAL, push = "main"), transformations = [url_transforms], mode = "SQUASH",)
# Import: external → internalcore.workflow( name = "import", origin = git.github_pr_origin(url = EXTERNAL, branch = "main"), destination = git.gerrit_destination(url = INTERNAL, ...), transformations = [url_transforms], # Uses reversal automatically mode = "CHANGE_REQUEST_FROM_SOT",)Preserving Authorship
Section titled “Preserving Authorship”External contributors should get credit:
authoring = authoring.pass_thru( default = "External <external@company.com>",)
transformations = [ # Preserve co-author information metadata.expose_label("Co-authored-by"), metadata.expose_label("Signed-off-by"),]Feedback Workflow
Section titled “Feedback Workflow”Notify when PRs are ready to import:
core.feedback( name = "notify-on-pr", origin = git.github_trigger( url = "https://github.com/company/public-repo", events = ["pull_request"], ), destination = git.github_api( url = "https://github.internal/internal-repo", ), actions = [ # Create internal issue when external PR is merged core.action(impl = _create_import_issue), ],)Complete Example
Section titled “Complete Example”# Full bidirectional workflowINTERNAL = "https://github.com/company/internal"EXTERNAL = "https://github.com/company/public"BOT = "Sync Bot <sync@company.com>"
core.workflow( name = "import-pr", origin = git.github_pr_origin( url = EXTERNAL, branch = "main", required_labels = ["approved"], ), destination = git.github_pr_destination( url = INTERNAL, destination_ref = "main", pr_branch = "import/external-${CONTEXT_REFERENCE}", title = "Import: External contribution", labels = ["external-contribution", "needs-review"], ), authoring = authoring.pass_thru(BOT), origin_files = glob(["src/**", "docs/**"]), transformations = [ core.move("", "external/"), metadata.expose_label("Co-authored-by"), metadata.add_header("Imported-From: external repository"), ], mode = "SQUASH",)