Skip to content

Working with Unsupported Platforms

Copybara provides first-class support for GitHub, GitLab, and Gerrit. For other platforms like Bitbucket, Azure DevOps, Gitea, or Codeberg, you can still use Copybara effectively with the right approach.

PlatformOriginDestinationPR/MR/CL Support
Generic Gitgit.origin()git.destination()No
GitHubgit.github_origin()git.github_destination()git.github_pr_destination()
GitLabgit.gitlab_origin()git.gitlab_mr_destination() (experimental)
Gerritgit.gerrit_origin()git.gerrit_destination()Built-in (CLs)

These popular platforms require workarounds for PR/MR creation:

Bitbucket

Atlassian’s Git hosting (Cloud and Server/Data Center)

Azure DevOps

Microsoft’s DevOps platform with Azure Repos

Gitea / Forgejo

Self-hosted, lightweight Git forges

Codeberg

Hosted Forgejo instance for open source

The right approach depends on your requirements:

If You Need…Use This Approach
Simplest possible setupDirect Push
Code review without automationFeature Branch + Manual PR
Scriptable PR creationPlatform CLI
Full Copybara integrationHTTP Endpoint
Existing CI infrastructureWebhook + CI
Simple wrapper scriptPost-Migration Script

Works for: All platforms Limitations: No PR/MR creation, bypasses code review

The simplest approach uses generic Git endpoints:

core.workflow(
name = "sync",
origin = git.origin(
url = "https://bitbucket.org/org/source-repo.git",
ref = "main",
),
destination = git.destination(
url = "https://bitbucket.org/org/dest-repo.git",
push = "main",
),
authoring = authoring.overwrite("Sync Bot <sync@example.com>"),
)

Pros:

  • Universal compatibility
  • Simple configuration
  • Works with SSH and HTTPS

Cons:

  • No PR/MR creation
  • Bypasses code review
  • May violate branch protection policies

Best for:

  • Mirror repositories between platforms
  • Push to unprotected branches
  • Internal syncs where review isn’t required

Works for: All platforms Effort: Low (manual step required)

Push to a feature branch, then create the PR manually in the platform’s UI:

destination = git.destination(
url = "https://bitbucket.org/org/repo.git",
push = "copybara/sync-latest", # Feature branch, not main
)

After Copybara runs, open the platform’s web UI and create a PR from copybara/sync-latest to main.

Pros:

  • Simple setup
  • Works with any platform
  • Respects code review workflow
  • Human oversight built in

Cons:

  • Manual PR creation step
  • Not fully automated

Best for:

  • Low-frequency syncs
  • When human oversight is desired anyway
  • Teams new to Copybara

Works for: Platforms with CLIs Effort: Medium

Run Copybara to push the branch, then use the platform’s CLI to create the PR:

sync-and-pr.sh
#!/bin/bash
# Step 1: Copybara pushes to feature branch
java -jar copybara.jar migrate copy.bara.sky sync
# Step 2: Create PR via platform CLI
Terminal window
# Install: pip install atlassian-python-api
# Or use the official CLI
bb pr create \
--source copybara/sync \
--destination main \
--title "Sync from source repository"
Terminal window
# Install: az extension add --name azure-devops
az repos pr create \
--source-branch copybara/sync \
--target-branch main \
--title "Sync from source repository"
Terminal window
# Install: go install code.gitea.io/tea@latest
tea pr create \
--head copybara/sync \
--base main \
--title "Sync from source repository"

Pros:

  • Scriptable and automatable
  • Uses official, supported tools
  • Can be integrated into CI

Cons:

  • Requires additional tooling
  • Two-step process
  • Platform-specific scripts

See Bitbucket, Azure DevOps, or Gitea for complete examples.


Works for: Any platform with REST API Effort: High (requires Starlark coding)

Use Copybara’s http.endpoint() to create PRs via the platform’s API:

def _create_pr(ctx):
response = ctx.destination.post(
url = "https://api.bitbucket.org/2.0/repositories/org/repo/pullrequests",
headers = {"Content-Type": "application/json"},
body = http.json({
"title": "Sync from source",
"source": {"branch": {"name": "copybara/sync"}},
"destination": {"branch": {"name": "main"}},
}),
)
return ctx.success() if response.status_code == 201 else ctx.error("PR creation failed")
core.feedback(
name = "create_pr",
origin = git.github_trigger(
url = "https://github.com/org/repo",
events = ["push"],
),
destination = http.endpoint(
hosts = [
http.host(
host = "api.bitbucket.org",
auth = http.bearer_auth(
creds = credentials.static_secret("bb_token", "BITBUCKET_TOKEN"),
),
),
],
),
actions = [core.action(impl = _create_pr)],
)

Pros:

  • Fully integrated with Copybara
  • Can be triggered automatically
  • Works with any REST API
  • Single tool manages everything

Cons:

  • Complex Starlark code
  • Requires understanding platform APIs
  • Harder to debug

See platform-specific guides for complete http.endpoint() examples.


Works for: Any platform with webhooks Effort: Medium-High

Let your CI system handle PR creation when Copybara pushes:

[Copybara] --push--> [Branch] --webhook--> [CI System] --API--> [Create PR]

Even if your destination is not GitHub, you can use GitHub Actions to create PRs elsewhere:

.github/workflows/create-pr.yml
name: Create PR on Bitbucket
on:
push:
branches: ["copybara/**"]
jobs:
create-pr:
runs-on: ubuntu-latest
steps:
- name: Create Bitbucket PR
run: |
curl -X POST \
-H "Authorization: Bearer ${{ secrets.BITBUCKET_TOKEN }}" \
-H "Content-Type: application/json" \
-d '{
"title": "Sync from source",
"source": {"branch": {"name": "${{ github.ref_name }}"}},
"destination": {"branch": {"name": "main"}}
}' \
https://api.bitbucket.org/2.0/repositories/org/repo/pullrequests

Pros:

  • Separation of concerns
  • Uses existing CI infrastructure
  • Easier to debug and modify
  • Familiar tooling for most teams

Cons:

  • Requires CI setup
  • More moving parts
  • Asynchronous (PR created after Copybara finishes)

Works for: All platforms Effort: Low-Medium

A simple shell script that runs Copybara then creates the PR:

sync-with-pr.sh
#!/bin/bash
set -e
PLATFORM="${PLATFORM:-bitbucket}"
SOURCE_BRANCH="copybara/sync"
TARGET_BRANCH="main"
# Run Copybara
java -jar copybara.jar migrate copy.bara.sky sync
# Check if changes were pushed
if git ls-remote --exit-code origin "$SOURCE_BRANCH" >/dev/null 2>&1; then
echo "Changes pushed to $SOURCE_BRANCH, creating PR..."
case "$PLATFORM" in
bitbucket)
curl -X POST \
-H "Authorization: Bearer $BITBUCKET_TOKEN" \
-H "Content-Type: application/json" \
-d "{\"title\":\"Sync\",\"source\":{\"branch\":{\"name\":\"$SOURCE_BRANCH\"}},\"destination\":{\"branch\":{\"name\":\"$TARGET_BRANCH\"}}}" \
"https://api.bitbucket.org/2.0/repositories/$WORKSPACE/$REPO/pullrequests"
;;
azure)
az repos pr create \
--source-branch "$SOURCE_BRANCH" \
--target-branch "$TARGET_BRANCH" \
--title "Sync from source"
;;
gitea)
curl -X POST \
-H "Authorization: token $GITEA_TOKEN" \
-H "Content-Type: application/json" \
-d "{\"title\":\"Sync\",\"head\":\"$SOURCE_BRANCH\",\"base\":\"$TARGET_BRANCH\"}" \
"$GITEA_URL/api/v1/repos/$OWNER/$REPO/pulls"
;;
esac
else
echo "No changes to sync"
fi

Pros:

  • Simple shell scripting
  • Easy to understand and modify
  • No Copybara changes needed
  • Portable across CI systems

Cons:

  • External to Copybara
  • Need to handle error cases manually
  • Less integrated

ApproachAutomationComplexityPR CreationBest For
Direct PushFullLowNoneMirrors, unprotected sync
Feature Branch + ManualPartialLowManualLow-frequency, human review
Platform CLIFullMediumAutomatedTeams familiar with CLIs
HTTP EndpointFullHighAutomatedFull Copybara integration
Webhook + CIFullMediumAutomatedExisting CI infrastructure
Wrapper ScriptFullLow-MediumAutomatedSimple, portable automation